@datasynx/agentic-crm 0.1.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +270 -669
- package/dist/{approvals-DpjxGHFp.js → approvals-CmDT2eUg.js} +7 -24
- package/dist/approvals-CmDT2eUg.js.map +1 -0
- package/dist/{ask-CID3jnuL.js → ask-CDysGnRg.js} +6 -6
- package/dist/{ask-CID3jnuL.js.map → ask-CDysGnRg.js.map} +1 -1
- package/dist/atomic-write-8yjqqLtS.js +29 -0
- package/dist/atomic-write-8yjqqLtS.js.map +1 -0
- package/dist/atomic-write-BYmF-ThH.cjs +37 -0
- package/dist/atomic-write-BYmF-ThH.cjs.map +1 -0
- package/dist/attachments-CX2GAtsw.cjs +517 -0
- package/dist/attachments-CX2GAtsw.cjs.map +1 -0
- package/dist/attachments-D207gXfN.js +514 -0
- package/dist/attachments-D207gXfN.js.map +1 -0
- package/dist/attachments-rLa96rOK.js +514 -0
- package/dist/attachments-rLa96rOK.js.map +1 -0
- package/dist/auth-B5DcjJ_6.js +2 -0
- package/dist/{auth-DFWwWcYD.js → auth-DDXZTwS0.js} +4 -13
- package/dist/auth-DDXZTwS0.js.map +1 -0
- package/dist/{autofill-Di_-SP7t.js → autofill-B9VtlR2j.js} +2 -2
- package/dist/{autofill-Di_-SP7t.js.map → autofill-B9VtlR2j.js.map} +1 -1
- package/dist/{backup-CeMk9z86.js → backup-CTlIxUdO.js} +5 -7
- package/dist/backup-CTlIxUdO.js.map +1 -0
- package/dist/backup-LFnC09oV.js +2 -0
- package/dist/chunk-BfDYWZQ8.cjs +32 -0
- package/dist/chunk-BfDYWZQ8.cjs.map +1 -0
- package/dist/chunk-BhUZmQg5.js +32 -0
- package/dist/chunk-BhUZmQg5.js.map +1 -0
- package/dist/chunk-ChC83jai.js +2 -0
- package/dist/chunk-e_w8qqtP.js +32 -0
- package/dist/chunk-e_w8qqtP.js.map +1 -0
- package/dist/{churn-C28IgnAj.js → churn-DN9WDGNM.js} +3 -3
- package/dist/{churn-C28IgnAj.js.map → churn-DN9WDGNM.js.map} +1 -1
- package/dist/cli.js +285 -186
- package/dist/cli.js.map +1 -1
- package/dist/{compliance-CKSBoQUe.js → compliance-Bc12Hn9a.js} +3 -3
- package/dist/{compliance-CKSBoQUe.js.map → compliance-Bc12Hn9a.js.map} +1 -1
- package/dist/{compliance-CujOqAKk.js → compliance-TqYQXhBj.js} +1 -1
- package/dist/{compliance-B1kk5-YS.js → compliance-kq0xHRw3.js} +3 -3
- package/dist/{compliance-B1kk5-YS.js.map → compliance-kq0xHRw3.js.map} +1 -1
- package/dist/{compliance-B91zNvCR.cjs → compliance-pAj9FcGI.cjs} +3 -3
- package/dist/{compliance-B91zNvCR.cjs.map → compliance-pAj9FcGI.cjs.map} +1 -1
- package/dist/{context-builder-BzWAp3Zs.js → context-builder-7Uab5-G4.js} +3 -2
- package/dist/context-builder-7Uab5-G4.js.map +1 -0
- package/dist/context-builder-hmOPvgso.js +2 -0
- package/dist/{custom-fields-CzNeD3_v.js → custom-fields-BMyz5Ruh.js} +1 -1
- package/dist/{custom-fields-Pl2t9xzp.js → custom-fields-GzpOHW_2.js} +4 -13
- package/dist/custom-fields-GzpOHW_2.js.map +1 -0
- package/dist/{custom-objects-CIFrmQ2V.js → custom-objects-BNy-ayR-.js} +1 -1
- package/dist/{custom-objects-BHgn1GEX.js → custom-objects-CxW1gHwJ.js} +10 -25
- package/dist/custom-objects-CxW1gHwJ.js.map +1 -0
- package/dist/{customer-dir-DIylZ8Q6.js → customer-dir-CkMMXhb0.js} +9 -4
- package/dist/customer-dir-CkMMXhb0.js.map +1 -0
- package/dist/daemon/worker.js +66 -40
- package/dist/daemon/worker.js.map +1 -1
- package/dist/doctor-C14-vnJ1.js +103 -0
- package/dist/doctor-C14-vnJ1.js.map +1 -0
- package/dist/email-body-BFSRa0AW.cjs +42 -0
- package/dist/email-body-BFSRa0AW.cjs.map +1 -0
- package/dist/email-body-BOd7U-D2.js +42 -0
- package/dist/email-body-BOd7U-D2.js.map +1 -0
- package/dist/{enrichment-3XvgGDfB.js → enrichment-CDFdWmvD.js} +3 -3
- package/dist/{enrichment-3XvgGDfB.js.map → enrichment-CDFdWmvD.js.map} +1 -1
- package/dist/{file-lock-B_zi7NQl.js → file-lock-CcHotQkZ.js} +3 -4
- package/dist/file-lock-CcHotQkZ.js.map +1 -0
- package/dist/{gmail-sync-DIaxInDT.js → gmail-sync-B4Iu3AQb.js} +56 -22
- package/dist/gmail-sync-B4Iu3AQb.js.map +1 -0
- package/dist/{gmail-sync-hHm9gaWd.cjs → gmail-sync-BpSVESSe.cjs} +55 -21
- package/dist/gmail-sync-BpSVESSe.cjs.map +1 -0
- package/dist/{gmail-sync-rQaVqKWd.js → gmail-sync-DIbrPnTK.js} +55 -21
- package/dist/gmail-sync-DIbrPnTK.js.map +1 -0
- package/dist/{gmail-webhook-handler-e5Od25FX.js → gmail-webhook-handler-BzOFbvgh.js} +4 -4
- package/dist/{gmail-webhook-handler-e5Od25FX.js.map → gmail-webhook-handler-BzOFbvgh.js.map} +1 -1
- package/dist/{gmail-webhook-handler-DS7OlRPX.js → gmail-webhook-handler-CvSDW_Js.js} +2 -2
- package/dist/{goal-engine-KpBftn4V.js → goal-engine-BbroPhqm.js} +10 -11
- package/dist/goal-engine-BbroPhqm.js.map +1 -0
- package/dist/{goal-engine-CUZSpERI.js → goal-engine-CfDAJTFt.js} +1 -1
- package/dist/{google-drive-sync-DEPcqFca.js → google-drive-sync-B_I1d54Y.js} +3 -3
- package/dist/{google-drive-sync-DEPcqFca.js.map → google-drive-sync-B_I1d54Y.js.map} +1 -1
- package/dist/html-BaeOCZKE.js +36 -0
- package/dist/html-BaeOCZKE.js.map +1 -0
- package/dist/html-CmOku6jS.cjs +47 -0
- package/dist/html-CmOku6jS.cjs.map +1 -0
- package/dist/{hygiene-DZqfYpFf.js → hygiene-DzQPnc6P.js} +3 -3
- package/dist/{hygiene-DZqfYpFf.js.map → hygiene-DzQPnc6P.js.map} +1 -1
- package/dist/identity-CB7j-Zr1.js +2 -0
- package/dist/{identity-CI6olMNm.js → identity-_uZ3Lbr2.js} +2 -2
- package/dist/{identity-CI6olMNm.js.map → identity-_uZ3Lbr2.js.map} +1 -1
- package/dist/{import-hubspot-BaK71U_K.js → import-hubspot-CTId9IGV.js} +51 -45
- package/dist/import-hubspot-CTId9IGV.js.map +1 -0
- package/dist/{index-YqwMd6aQ.d.cts → index-BAutNcAT.d.cts} +20 -12
- package/dist/index-BAutNcAT.d.cts.map +1 -0
- package/dist/{index-V8BFaH-b.d.ts → index-FzDsNSSb.d.ts} +12 -4
- package/dist/index-FzDsNSSb.d.ts.map +1 -0
- package/dist/index.cjs +19 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -12
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +12 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -21
- package/dist/index.js.map +1 -1
- package/dist/interactions-writer-B2y-73lh.js +2 -0
- package/dist/{interactions-writer-SLHnoEeE.js → interactions-writer-B8XAzdqR.js} +34 -4
- package/dist/interactions-writer-B8XAzdqR.js.map +1 -0
- package/dist/{interactions-writer-CrPStUll.cjs → interactions-writer-BRJNrefF.cjs} +7 -3
- package/dist/interactions-writer-BRJNrefF.cjs.map +1 -0
- package/dist/{interactions-writer-DO3KcSR3.js → interactions-writer-ZQcpFOh9.js} +7 -3
- package/dist/interactions-writer-ZQcpFOh9.js.map +1 -0
- package/dist/json-store-WWsFzXub.js +43 -0
- package/dist/json-store-WWsFzXub.js.map +1 -0
- package/dist/{knowledge-base-D0Fh40kc.js → knowledge-base--063Kpa3.js} +51 -22
- package/dist/knowledge-base--063Kpa3.js.map +1 -0
- package/dist/{lancedb-CCBbpulq.js → lancedb-CswQEE5K.js} +1 -1
- package/dist/{lancedb-rlvWoPwl.js → lancedb-CuHKNsNZ.js} +4 -3
- package/dist/lancedb-CuHKNsNZ.js.map +1 -0
- package/dist/{lead-model-BCFzyktm.js → lead-model-CEmx7te7.js} +6 -14
- package/dist/lead-model-CEmx7te7.js.map +1 -0
- package/dist/{llm-Z8RIYkpF.js → llm-BnSUBisu.js} +2 -2
- package/dist/{llm-Z8RIYkpF.js.map → llm-BnSUBisu.js.map} +1 -1
- package/dist/{llm-iijeXmgq.cjs → llm-CXycmEl9.cjs} +2 -2
- package/dist/{llm-iijeXmgq.cjs.map → llm-CXycmEl9.cjs.map} +1 -1
- package/dist/{llm-DEjWcqmW.js → llm-DSX1-wFu.js} +1 -1
- package/dist/{llm-DvzZqva0.js → llm-PZzgPphl.js} +3 -3
- package/dist/{llm-DvzZqva0.js.map → llm-PZzgPphl.js.map} +1 -1
- package/dist/logger-BkInaGoV.cjs +167 -0
- package/dist/logger-BkInaGoV.cjs.map +1 -0
- package/dist/logger-Dyl4VcLO.js +147 -0
- package/dist/logger-Dyl4VcLO.js.map +1 -0
- package/dist/logger-UaF5p9d1.js +147 -0
- package/dist/logger-UaF5p9d1.js.map +1 -0
- package/dist/logger-vKQS34w9.js +2 -0
- package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
- package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
- package/dist/mcp.cjs +365 -319
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.d.cts.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +365 -319
- package/dist/mcp.js.map +1 -1
- package/dist/{memory-Cy6-Tbyl.js → memory-D8hmgD9d.js} +1 -1
- package/dist/{memory-Bb6ky3kb.js → memory-Dzr9dXSM.js} +4 -11
- package/dist/memory-Dzr9dXSM.js.map +1 -0
- package/dist/{microsoft-calendar-B6MMtUQK.js → microsoft-calendar-BgVR8GDv.js} +4 -4
- package/dist/{microsoft-calendar-B6MMtUQK.js.map → microsoft-calendar-BgVR8GDv.js.map} +1 -1
- package/dist/{microsoft-sync-CpZVoSuq.js → microsoft-sync-D30_XksI.js} +5 -5
- package/dist/{microsoft-sync-CpZVoSuq.js.map → microsoft-sync-D30_XksI.js.map} +1 -1
- package/dist/{nba-3wanmJ0U.js → nba-DwdfM93s.js} +3 -3
- package/dist/{nba-3wanmJ0U.js.map → nba-DwdfM93s.js.map} +1 -1
- package/dist/{notification-dispatcher-0vYNngWe.js → notification-dispatcher-inpKyuBz.js} +7 -3
- package/dist/notification-dispatcher-inpKyuBz.js.map +1 -0
- package/dist/{pipeline-writer-BqBrYrQc.js → pipeline-writer-0LJ6Qkat.js} +1 -1
- package/dist/{pipeline-writer-N2omexxp.cjs → pipeline-writer-B1tRAhuD.cjs} +11 -3
- package/dist/pipeline-writer-B1tRAhuD.cjs.map +1 -0
- package/dist/{pipeline-writer-BvVquKIe.js → pipeline-writer-CIllfnZl.js} +5 -3
- package/dist/pipeline-writer-CIllfnZl.js.map +1 -0
- package/dist/{pipeline-writer-eufx_0o1.js → pipeline-writer-rDj-ni6q.js} +6 -4
- package/dist/pipeline-writer-rDj-ni6q.js.map +1 -0
- package/dist/{proactive-agent-BgQXw3ac.js → proactive-agent-B7u3Bj_l.js} +6 -6
- package/dist/{proactive-agent-BgQXw3ac.js.map → proactive-agent-B7u3Bj_l.js.map} +1 -1
- package/dist/{proactive-worker-BrLHNhjH.js → proactive-worker-1zkm6aJD.js} +7 -8
- package/dist/proactive-worker-1zkm6aJD.js.map +1 -0
- package/dist/{push-manager-CowY-0IK.js → push-manager-BXM-IHfP.js} +1 -1
- package/dist/{push-manager-CdqIIkuh.js → push-manager-C0ECQgva.js} +4 -4
- package/dist/push-manager-C0ECQgva.js.map +1 -0
- package/dist/{quote-generator-OhSFsi3x.js → quote-generator-ByUyIYtw.js} +1 -1
- package/dist/{quote-generator-BfwENXzg.js → quote-generator-CTdR8eEI.js} +5 -5
- package/dist/quote-generator-CTdR8eEI.js.map +1 -0
- package/dist/rbac-DzbyFhVH.js +2 -0
- package/dist/{rbac-CTIktZaC.js → rbac-msmBc_tK.js} +19 -12
- package/dist/rbac-msmBc_tK.js.map +1 -0
- package/dist/regex-Jt5DatPi.js +13 -0
- package/dist/regex-Jt5DatPi.js.map +1 -0
- package/dist/{relationship-health-odxEoQdJ.js → relationship-health-ZZNXR1RZ.js} +8 -16
- package/dist/relationship-health-ZZNXR1RZ.js.map +1 -0
- package/dist/{revenue-simulation-Bqf2DLVB.js → revenue-simulation-D8f_YkUY.js} +9 -19
- package/dist/revenue-simulation-D8f_YkUY.js.map +1 -0
- package/dist/{revenue-simulation-BJdRTEHc.js → revenue-simulation-njJZlTqm.js} +1 -1
- package/dist/safe-path-mpp0dKtO.js +18 -0
- package/dist/safe-path-mpp0dKtO.js.map +1 -0
- package/dist/{segments-BqcD5HIl.js → segments-DI3LOQNe.js} +5 -14
- package/dist/segments-DI3LOQNe.js.map +1 -0
- package/dist/sequence-engine-C6nnewHX.js +2 -0
- package/dist/{sequence-engine-J1lTW_in.js → sequence-engine-DNTVLq7o.js} +15 -8
- package/dist/sequence-engine-DNTVLq7o.js.map +1 -0
- package/dist/{sequence-store-DaaWr0Os.js → sequence-store-CmYb6s0g.js} +6 -5
- package/dist/sequence-store-CmYb6s0g.js.map +1 -0
- package/dist/{server-Dyva03K8.js → server-DoRPPOeR.js} +308 -230
- package/dist/server-DoRPPOeR.js.map +1 -0
- package/dist/{session-D9ub6Wl1.js → session-B6XaP83h.js} +3 -3
- package/dist/session-B6XaP83h.js.map +1 -0
- package/dist/{session-B9AilxOE.js → session-BgGDyP2C.js} +3 -3
- package/dist/session-BgGDyP2C.js.map +1 -0
- package/dist/session-Bp4zTh4l.js +2 -0
- package/dist/{session-D0qFkBla.cjs → session-Mm7GQbSH.cjs} +3 -3
- package/dist/session-Mm7GQbSH.cjs.map +1 -0
- package/dist/{session-store-C8tEvMPw.js → session-store-DWxJ5Pof.js} +79 -17
- package/dist/session-store-DWxJ5Pof.js.map +1 -0
- package/dist/{session-store-B0QZE8Bx.cjs → session-store-yfwnj0OC.cjs} +126 -16
- package/dist/session-store-yfwnj0OC.cjs.map +1 -0
- package/dist/{sla-engine-5IhTsBUR.js → sla-engine-CP2KiKDS.js} +1 -1
- package/dist/{sla-engine-BqX-7u-7.js → sla-engine-O-A1ntu_.js} +2 -2
- package/dist/{sla-engine-BqX-7u-7.js.map → sla-engine-O-A1ntu_.js.map} +1 -1
- package/dist/{sop-Vp0UPWFW.js → sop-BV7ICAFR.js} +4 -11
- package/dist/sop-BV7ICAFR.js.map +1 -0
- package/dist/{sop-DkhVChGy.js → sop-D33qTHUb.js} +1 -1
- package/dist/survey-engine-DKctGcLQ.js +2 -0
- package/dist/{survey-engine-DBjCYqCv.js → survey-engine-DngXBv47.js} +5 -4
- package/dist/survey-engine-DngXBv47.js.map +1 -0
- package/dist/{sync-state-CwLSt_1m.js → sync-state-BaA8LbTI.js} +1 -1
- package/dist/{sync-state-ChaLbamC.js → sync-state-DMZgzpez.js} +4 -12
- package/dist/sync-state-DMZgzpez.js.map +1 -0
- package/dist/{ticket-writer-CjqKeIRD.js → ticket-writer-DsfpeLGZ.js} +1 -1
- package/dist/{ticket-writer-j2oX_Wal.js → ticket-writer-a9on36Wb.js} +12 -24
- package/dist/ticket-writer-a9on36Wb.js.map +1 -0
- package/dist/{tone-Bdm5uaht.js → tone-C7bqK69y.js} +5 -12
- package/dist/tone-C7bqK69y.js.map +1 -0
- package/dist/{tone-DRKlZgPr.cjs → tone-Cmc7O2Fx.cjs} +3 -9
- package/dist/tone-Cmc7O2Fx.cjs.map +1 -0
- package/dist/{tone-vNb2DAAD.js → tone-mXSftvTn.js} +3 -8
- package/dist/tone-mXSftvTn.js.map +1 -0
- package/dist/{transcript-watcher-CL2QUygI.js → transcript-watcher-BoClrJAz.js} +18 -11
- package/dist/transcript-watcher-BoClrJAz.js.map +1 -0
- package/dist/unmatched-transcripts-C92zAoM4.js +2 -0
- package/dist/unmatched-transcripts-DC-VQ9YS.js +16 -0
- package/dist/unmatched-transcripts-DC-VQ9YS.js.map +1 -0
- package/dist/update-deal-CWy1eLJI.js +2 -0
- package/dist/{update-deal-DKC79skb.js → update-deal-DSzr_Aau.js} +3 -3
- package/dist/{update-deal-DKC79skb.js.map → update-deal-DSzr_Aau.js.map} +1 -1
- package/dist/{usage-D0-TYJkw.js → usage-BVlFlKW_.js} +8 -6
- package/dist/usage-BVlFlKW_.js.map +1 -0
- package/dist/usage-CClTf5e6.cjs.map +1 -1
- package/dist/usage-D0u9a-lV.js.map +1 -1
- package/dist/{vault-DXCg29W-.js → vault-CfwZdNzC.js} +3 -4
- package/dist/vault-CfwZdNzC.js.map +1 -0
- package/dist/{vault-C1D3zScD.js → vault-DxKP4_R2.js} +1 -1
- package/dist/{webhooks-Xn6zO6kd.cjs → webhooks-CwW-3kvG.cjs} +5 -19
- package/dist/webhooks-CwW-3kvG.cjs.map +1 -0
- package/dist/{webhooks-7EpA05Qr.js → webhooks-DXr1IoKn.js} +8 -21
- package/dist/webhooks-DXr1IoKn.js.map +1 -0
- package/dist/{webhooks-BO2UAnmn.js → webhooks-sWZ8CJtR.js} +5 -18
- package/dist/webhooks-sWZ8CJtR.js.map +1 -0
- package/package.json +22 -2
- package/dist/approvals-DpjxGHFp.js.map +0 -1
- package/dist/auth-CyFuu9X_.js +0 -2
- package/dist/auth-DFWwWcYD.js.map +0 -1
- package/dist/backup-CeMk9z86.js.map +0 -1
- package/dist/backup-f_hC7rBV.js +0 -2
- package/dist/context-builder-BzWAp3Zs.js.map +0 -1
- package/dist/context-builder-DlrRcqmJ.js +0 -2
- package/dist/custom-fields-Pl2t9xzp.js.map +0 -1
- package/dist/custom-objects-BHgn1GEX.js.map +0 -1
- package/dist/customer-dir-DIylZ8Q6.js.map +0 -1
- package/dist/file-lock-B_zi7NQl.js.map +0 -1
- package/dist/gmail-sync-DIaxInDT.js.map +0 -1
- package/dist/gmail-sync-hHm9gaWd.cjs.map +0 -1
- package/dist/gmail-sync-rQaVqKWd.js.map +0 -1
- package/dist/goal-engine-KpBftn4V.js.map +0 -1
- package/dist/identity-gyfWdrcX.js +0 -2
- package/dist/import-hubspot-BaK71U_K.js.map +0 -1
- package/dist/index-V8BFaH-b.d.ts.map +0 -1
- package/dist/index-YqwMd6aQ.d.cts.map +0 -1
- package/dist/interactions-writer-CrPStUll.cjs.map +0 -1
- package/dist/interactions-writer-DO3KcSR3.js.map +0 -1
- package/dist/interactions-writer-SLHnoEeE.js.map +0 -1
- package/dist/interactions-writer-dSPy1XfO.js +0 -2
- package/dist/knowledge-base-D0Fh40kc.js.map +0 -1
- package/dist/lancedb-rlvWoPwl.js.map +0 -1
- package/dist/lead-model-BCFzyktm.js.map +0 -1
- package/dist/memory-Bb6ky3kb.js.map +0 -1
- package/dist/notification-dispatcher-0vYNngWe.js.map +0 -1
- package/dist/pipeline-writer-BvVquKIe.js.map +0 -1
- package/dist/pipeline-writer-N2omexxp.cjs.map +0 -1
- package/dist/pipeline-writer-eufx_0o1.js.map +0 -1
- package/dist/proactive-worker-BrLHNhjH.js.map +0 -1
- package/dist/push-manager-CdqIIkuh.js.map +0 -1
- package/dist/quote-generator-BfwENXzg.js.map +0 -1
- package/dist/rbac-C7c8tcES.js +0 -2
- package/dist/rbac-CTIktZaC.js.map +0 -1
- package/dist/relationship-health-odxEoQdJ.js.map +0 -1
- package/dist/revenue-simulation-Bqf2DLVB.js.map +0 -1
- package/dist/segments-BqcD5HIl.js.map +0 -1
- package/dist/sequence-engine-CCTHEBgi.js +0 -2
- package/dist/sequence-engine-J1lTW_in.js.map +0 -1
- package/dist/sequence-store-DaaWr0Os.js.map +0 -1
- package/dist/server-Dyva03K8.js.map +0 -1
- package/dist/session-B9AilxOE.js.map +0 -1
- package/dist/session-D0qFkBla.cjs.map +0 -1
- package/dist/session-D9ub6Wl1.js.map +0 -1
- package/dist/session-mWHA71Lw.js +0 -2
- package/dist/session-store-B0QZE8Bx.cjs.map +0 -1
- package/dist/session-store-C8tEvMPw.js.map +0 -1
- package/dist/sop-Vp0UPWFW.js.map +0 -1
- package/dist/survey-engine-C06hcQt3.js +0 -2
- package/dist/survey-engine-DBjCYqCv.js.map +0 -1
- package/dist/sync-state-ChaLbamC.js.map +0 -1
- package/dist/ticket-writer-j2oX_Wal.js.map +0 -1
- package/dist/tone-Bdm5uaht.js.map +0 -1
- package/dist/tone-DRKlZgPr.cjs.map +0 -1
- package/dist/tone-vNb2DAAD.js.map +0 -1
- package/dist/transcript-watcher-CL2QUygI.js.map +0 -1
- package/dist/unmatched-transcripts-BsH5bhkU.js +0 -26
- package/dist/unmatched-transcripts-BsH5bhkU.js.map +0 -1
- package/dist/unmatched-transcripts-D0PrJ9iz.js +0 -2
- package/dist/update-deal-BNwPGaTV.js +0 -2
- package/dist/usage-D0-TYJkw.js.map +0 -1
- package/dist/vault-DXCg29W-.js.map +0 -1
- package/dist/webhooks-7EpA05Qr.js.map +0 -1
- package/dist/webhooks-BO2UAnmn.js.map +0 -1
- package/dist/webhooks-Xn6zO6kd.cjs.map +0 -1
|
@@ -76,7 +76,7 @@ const DISCLOSURES = {
|
|
|
76
76
|
en: "Note: This content was generated with the help of AI (EU AI Act Art. 50)."
|
|
77
77
|
};
|
|
78
78
|
/** Localized Art. 50 disclosure string. */
|
|
79
|
-
function aiDisclosure(lang = "
|
|
79
|
+
function aiDisclosure(lang = "en") {
|
|
80
80
|
return DISCLOSURES[lang] ?? DISCLOSURES.en;
|
|
81
81
|
}
|
|
82
82
|
/** Disclosure is ON by default (Art. 50); opt out with DXCRM_AI_DISCLOSURE=off. */
|
|
@@ -86,7 +86,7 @@ function aiDisclosureEnabled() {
|
|
|
86
86
|
/** Prepend the Art. 50 disclosure to generated content (unless disabled). */
|
|
87
87
|
function labelAiContent(text, opts = {}) {
|
|
88
88
|
if (!(opts.enabled ?? aiDisclosureEnabled())) return text;
|
|
89
|
-
return `${aiDisclosure(opts.lang ?? (process.env["DXCRM_AI_DISCLOSURE_LANG"] || "
|
|
89
|
+
return `${aiDisclosure(opts.lang ?? (process.env["DXCRM_AI_DISCLOSURE_LANG"] || "en"))}\n\n${text}`;
|
|
90
90
|
}
|
|
91
91
|
/** Resolve the configured LLM provider (default Anthropic). */
|
|
92
92
|
function llmProvider() {
|
|
@@ -115,4 +115,4 @@ function complianceConfig() {
|
|
|
115
115
|
//#endregion
|
|
116
116
|
export { llmProvider as a, neutralizeUntrusted as c, labelAiContent as i, maskPii as l, aiDisclosureEnabled as n, localLlmConfig as o, complianceConfig as r, guardrailsEnabled as s, aiDisclosure as t, piiMaskingEnabled as u };
|
|
117
117
|
|
|
118
|
-
//# sourceMappingURL=compliance-
|
|
118
|
+
//# sourceMappingURL=compliance-Bc12Hn9a.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compliance-
|
|
1
|
+
{"version":3,"file":"compliance-Bc12Hn9a.js","names":[],"sources":["../src/core/pii.ts","../src/core/guardrails.ts","../src/core/compliance.ts"],"sourcesContent":["/**\n * Lightweight PII masking for LLM calls (Einstein-Trust-Layer-style).\n * Replaces emails and phone numbers with stable placeholders before text is\n * sent to an LLM, and can restore them in the response. Opt-in via\n * DXCRM_PII_MASKING=on so default behaviour and quality are unchanged.\n */\n\nconst EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g;\n// Phone: optional +, then 7+ digits possibly grouped by spaces/dashes/parens.\nconst PHONE_RE = /\\+?\\d[\\d\\s().-]{6,}\\d/g;\n\nexport interface MaskedText {\n masked: string;\n unmask: (text: string) => string;\n}\n\nexport function piiMaskingEnabled(): boolean {\n return process.env[\"DXCRM_PII_MASKING\"] === \"on\";\n}\n\nexport function maskPii(input: string): MaskedText {\n const mapping = new Map<string, string>(); // original -> placeholder\n let emailCount = 0;\n let phoneCount = 0;\n\n let masked = input.replace(EMAIL_RE, (match) => {\n let ph = mapping.get(match);\n if (!ph) {\n ph = `[EMAIL_${emailCount++}]`;\n mapping.set(match, ph);\n }\n return ph;\n });\n\n masked = masked.replace(PHONE_RE, (match) => {\n // Ignore very short numeric runs that slipped through (already handled by {6,}).\n let ph = mapping.get(match);\n if (!ph) {\n ph = `[PHONE_${phoneCount++}]`;\n mapping.set(match, ph);\n }\n return ph;\n });\n\n const reverse = new Map<string, string>(); // placeholder -> original\n for (const [orig, ph] of mapping) reverse.set(ph, orig);\n\n const unmask = (text: string): string => {\n let out = text;\n for (const [ph, orig] of reverse) out = out.split(ph).join(orig);\n return out;\n };\n\n return { masked, unmask };\n}\n","/**\n * Guardrails against (indirect) prompt injection in untrusted CRM content\n * (emails, transcripts, imported notes). Detection is always available;\n * neutralization is applied to LLM inputs when DXCRM_GUARDRAILS=on.\n */\n\nconst INJECTION_PATTERNS: RegExp[] = [\n /ignore\\s+(all\\s+|the\\s+|any\\s+)?(previous|above|prior|preceding)\\s+(instructions?|prompts?|messages?)/gi,\n /disregard\\s+(all\\s+|the\\s+|any\\s+)?(previous|above|prior|preceding)/gi,\n /forget\\s+(all\\s+|the\\s+|everything\\s+)?(previous|above|prior|you were told)/gi,\n /you\\s+are\\s+now\\b/gi,\n /reveal\\s+(your|the)\\s+(system\\s+)?(instructions?|prompt|prompts?)/gi,\n /(print|show|repeat)\\s+(your|the)\\s+(system\\s+)?(instructions?|prompt)/gi,\n /act\\s+as\\s+(an?\\s+)?(system|admin|administrator|developer|root)\\b/gi,\n /<\\/?(system|assistant|tool)\\b[^>]*>/gi,\n /\\bBEGIN\\s+SYSTEM\\b/gi,\n];\n\nexport function guardrailsEnabled(): boolean {\n return process.env[\"DXCRM_GUARDRAILS\"] === \"on\";\n}\n\nexport function detectPromptInjection(text: string): { flagged: boolean; matches: string[] } {\n const matches: string[] = [];\n for (const re of INJECTION_PATTERNS) {\n re.lastIndex = 0;\n const found = text.match(re);\n if (found) matches.push(...found);\n }\n return { flagged: matches.length > 0, matches };\n}\n\n/** Replace known injection spans with a [filtered] marker, keeping other text. */\nexport function neutralizeUntrusted(text: string): string {\n let out = text;\n for (const re of INJECTION_PATTERNS) {\n out = out.replace(re, \"[filtered]\");\n }\n return out;\n}\n","import { piiMaskingEnabled } from \"./pii.js\";\nimport { guardrailsEnabled } from \"./guardrails.js\";\n\n/**\n * Compliance hardening (domino D17 / §3): the cross-cutting governance layer.\n * - EU-AI-Act Art. 50 transparency: a localized disclosure that AI generated\n * the content, on by default (opt-out), wrappable around any generated text.\n * - Provider-agnostic LLM selection so the workspace can run against a local,\n * self-hosted model (Ollama / OpenAI-compatible) as a data-residency moat —\n * no customer data leaves the machine. The provider runtime itself stays in\n * the agent framework; here we only resolve and expose the configuration.\n * - A single read-out of the active privacy/compliance posture.\n */\nexport type DisclosureLang = \"de\" | \"en\";\n\nexport type LlmProviderName = \"anthropic\" | \"ollama\" | \"openai\" | \"local\";\n\nconst DISCLOSURES: Record<DisclosureLang, string> = {\n de: \"Hinweis: Dieser Inhalt wurde mithilfe von KI erstellt (EU-AI-Act Art. 50).\",\n en: \"Note: This content was generated with the help of AI (EU AI Act Art. 50).\",\n};\n\n/** Localized Art. 50 disclosure string. */\nexport function aiDisclosure(lang: DisclosureLang = \"en\"): string {\n return DISCLOSURES[lang] ?? DISCLOSURES.en;\n}\n\n/** Disclosure is ON by default (Art. 50); opt out with DXCRM_AI_DISCLOSURE=off. */\nexport function aiDisclosureEnabled(): boolean {\n return process.env[\"DXCRM_AI_DISCLOSURE\"] !== \"off\";\n}\n\n/** Prepend the Art. 50 disclosure to generated content (unless disabled). */\nexport function labelAiContent(\n text: string,\n opts: { lang?: DisclosureLang; enabled?: boolean } = {}\n): string {\n const enabled = opts.enabled ?? aiDisclosureEnabled();\n if (!enabled) return text;\n const lang = opts.lang ?? ((process.env[\"DXCRM_AI_DISCLOSURE_LANG\"] as DisclosureLang) || \"en\");\n return `${aiDisclosure(lang)}\\n\\n${text}`;\n}\n\n/** Resolve the configured LLM provider (default Anthropic). */\nexport function llmProvider(): LlmProviderName {\n const p = (process.env[\"DXCRM_LLM_PROVIDER\"] ?? \"anthropic\").toLowerCase();\n if (p === \"ollama\" || p === \"openai\" || p === \"local\") return p;\n return \"anthropic\";\n}\n\nexport interface LocalLlmConfig {\n baseUrl: string;\n model: string;\n}\n\n/** OpenAI-compatible local endpoint config (Ollama defaults), env-overridable. */\nexport function localLlmConfig(): LocalLlmConfig {\n return {\n baseUrl: process.env[\"DXCRM_LLM_BASE_URL\"] ?? \"http://127.0.0.1:11434/v1\",\n model: process.env[\"DXCRM_LLM_MODEL\"] ?? \"llama3.1\",\n };\n}\n\nexport interface ComplianceConfig {\n provider: LlmProviderName;\n local: LocalLlmConfig | null;\n aiDisclosure: boolean;\n piiMasking: boolean;\n guardrails: boolean;\n}\n\n/** A single read-out of the active privacy/compliance posture. */\nexport function complianceConfig(): ComplianceConfig {\n const provider = llmProvider();\n return {\n provider,\n local: provider === \"anthropic\" ? null : localLlmConfig(),\n aiDisclosure: aiDisclosureEnabled(),\n piiMasking: piiMaskingEnabled(),\n guardrails: guardrailsEnabled(),\n };\n}\n"],"mappings":";;;;;;;AAOA,MAAM,WAAW;AAEjB,MAAM,WAAW;AAOjB,SAAgB,oBAA6B;CAC3C,OAAO,QAAQ,IAAI,yBAAyB;AAC9C;AAEA,SAAgB,QAAQ,OAA2B;CACjD,MAAM,0BAAU,IAAI,IAAoB;CACxC,IAAI,aAAa;CACjB,IAAI,aAAa;CAEjB,IAAI,SAAS,MAAM,QAAQ,WAAW,UAAU;EAC9C,IAAI,KAAK,QAAQ,IAAI,KAAK;EAC1B,IAAI,CAAC,IAAI;GACP,KAAK,UAAU,aAAa;GAC5B,QAAQ,IAAI,OAAO,EAAE;EACvB;EACA,OAAO;CACT,CAAC;CAED,SAAS,OAAO,QAAQ,WAAW,UAAU;EAE3C,IAAI,KAAK,QAAQ,IAAI,KAAK;EAC1B,IAAI,CAAC,IAAI;GACP,KAAK,UAAU,aAAa;GAC5B,QAAQ,IAAI,OAAO,EAAE;EACvB;EACA,OAAO;CACT,CAAC;CAED,MAAM,0BAAU,IAAI,IAAoB;CACxC,KAAK,MAAM,CAAC,MAAM,OAAO,SAAS,QAAQ,IAAI,IAAI,IAAI;CAEtD,MAAM,UAAU,SAAyB;EACvC,IAAI,MAAM;EACV,KAAK,MAAM,CAAC,IAAI,SAAS,SAAS,MAAM,IAAI,MAAM,EAAE,EAAE,KAAK,IAAI;EAC/D,OAAO;CACT;CAEA,OAAO;EAAE;EAAQ;CAAO;AAC1B;;;;;;;;AChDA,MAAM,qBAA+B;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAgB,oBAA6B;CAC3C,OAAO,QAAQ,IAAI,wBAAwB;AAC7C;;AAaA,SAAgB,oBAAoB,MAAsB;CACxD,IAAI,MAAM;CACV,KAAK,MAAM,MAAM,oBACf,MAAM,IAAI,QAAQ,IAAI,YAAY;CAEpC,OAAO;AACT;;;ACtBA,MAAM,cAA8C;CAClD,IAAI;CACJ,IAAI;AACN;;AAGA,SAAgB,aAAa,OAAuB,MAAc;CAChE,OAAO,YAAY,SAAS,YAAY;AAC1C;;AAGA,SAAgB,sBAA+B;CAC7C,OAAO,QAAQ,IAAI,2BAA2B;AAChD;;AAGA,SAAgB,eACd,MACA,OAAqD,CAAC,GAC9C;CAER,IAAI,EADY,KAAK,WAAW,oBAAoB,IACtC,OAAO;CAErB,OAAO,GAAG,aADG,KAAK,SAAU,QAAQ,IAAI,+BAAkD,KAC/D,EAAE,MAAM;AACrC;;AAGA,SAAgB,cAA+B;CAC7C,MAAM,KAAK,QAAQ,IAAI,yBAAyB,aAAa,YAAY;CACzE,IAAI,MAAM,YAAY,MAAM,YAAY,MAAM,SAAS,OAAO;CAC9D,OAAO;AACT;;AAQA,SAAgB,iBAAiC;CAC/C,OAAO;EACL,SAAS,QAAQ,IAAI,yBAAyB;EAC9C,OAAO,QAAQ,IAAI,sBAAsB;CAC3C;AACF;;AAWA,SAAgB,mBAAqC;CACnD,MAAM,WAAW,YAAY;CAC7B,OAAO;EACL;EACA,OAAO,aAAa,cAAc,OAAO,eAAe;EACxD,cAAc,oBAAoB;EAClC,YAAY,kBAAkB;EAC9B,YAAY,kBAAkB;CAChC;AACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as labelAiContent, r as complianceConfig, t as aiDisclosure } from "./compliance-
|
|
1
|
+
import { i as labelAiContent, r as complianceConfig, t as aiDisclosure } from "./compliance-Bc12Hn9a.js";
|
|
2
2
|
export { aiDisclosure, complianceConfig, labelAiContent };
|
|
@@ -84,7 +84,7 @@ const DISCLOSURES = {
|
|
|
84
84
|
en: "Note: This content was generated with the help of AI (EU AI Act Art. 50)."
|
|
85
85
|
};
|
|
86
86
|
/** Localized Art. 50 disclosure string. */
|
|
87
|
-
function aiDisclosure(lang = "
|
|
87
|
+
function aiDisclosure(lang = "en") {
|
|
88
88
|
return DISCLOSURES[lang] ?? DISCLOSURES.en;
|
|
89
89
|
}
|
|
90
90
|
/** Disclosure is ON by default (Art. 50); opt out with DXCRM_AI_DISCLOSURE=off. */
|
|
@@ -94,7 +94,7 @@ function aiDisclosureEnabled() {
|
|
|
94
94
|
/** Prepend the Art. 50 disclosure to generated content (unless disabled). */
|
|
95
95
|
function labelAiContent(text, opts = {}) {
|
|
96
96
|
if (!(opts.enabled ?? aiDisclosureEnabled())) return text;
|
|
97
|
-
return `${aiDisclosure(opts.lang ?? (process.env["DXCRM_AI_DISCLOSURE_LANG"] || "
|
|
97
|
+
return `${aiDisclosure(opts.lang ?? (process.env["DXCRM_AI_DISCLOSURE_LANG"] || "en"))}\n\n${text}`;
|
|
98
98
|
}
|
|
99
99
|
/** Resolve the configured LLM provider (default Anthropic). */
|
|
100
100
|
function llmProvider() {
|
|
@@ -112,4 +112,4 @@ function localLlmConfig() {
|
|
|
112
112
|
//#endregion
|
|
113
113
|
export { neutralizeUntrusted as a, guardrailsEnabled as i, llmProvider as n, maskPii as o, localLlmConfig as r, piiMaskingEnabled as s, compliance_exports as t };
|
|
114
114
|
|
|
115
|
-
//# sourceMappingURL=compliance-
|
|
115
|
+
//# sourceMappingURL=compliance-kq0xHRw3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compliance-
|
|
1
|
+
{"version":3,"file":"compliance-kq0xHRw3.js","names":[],"sources":["../src/core/pii.ts","../src/core/guardrails.ts","../src/core/compliance.ts"],"sourcesContent":["/**\n * Lightweight PII masking for LLM calls (Einstein-Trust-Layer-style).\n * Replaces emails and phone numbers with stable placeholders before text is\n * sent to an LLM, and can restore them in the response. Opt-in via\n * DXCRM_PII_MASKING=on so default behaviour and quality are unchanged.\n */\n\nconst EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g;\n// Phone: optional +, then 7+ digits possibly grouped by spaces/dashes/parens.\nconst PHONE_RE = /\\+?\\d[\\d\\s().-]{6,}\\d/g;\n\nexport interface MaskedText {\n masked: string;\n unmask: (text: string) => string;\n}\n\nexport function piiMaskingEnabled(): boolean {\n return process.env[\"DXCRM_PII_MASKING\"] === \"on\";\n}\n\nexport function maskPii(input: string): MaskedText {\n const mapping = new Map<string, string>(); // original -> placeholder\n let emailCount = 0;\n let phoneCount = 0;\n\n let masked = input.replace(EMAIL_RE, (match) => {\n let ph = mapping.get(match);\n if (!ph) {\n ph = `[EMAIL_${emailCount++}]`;\n mapping.set(match, ph);\n }\n return ph;\n });\n\n masked = masked.replace(PHONE_RE, (match) => {\n // Ignore very short numeric runs that slipped through (already handled by {6,}).\n let ph = mapping.get(match);\n if (!ph) {\n ph = `[PHONE_${phoneCount++}]`;\n mapping.set(match, ph);\n }\n return ph;\n });\n\n const reverse = new Map<string, string>(); // placeholder -> original\n for (const [orig, ph] of mapping) reverse.set(ph, orig);\n\n const unmask = (text: string): string => {\n let out = text;\n for (const [ph, orig] of reverse) out = out.split(ph).join(orig);\n return out;\n };\n\n return { masked, unmask };\n}\n","/**\n * Guardrails against (indirect) prompt injection in untrusted CRM content\n * (emails, transcripts, imported notes). Detection is always available;\n * neutralization is applied to LLM inputs when DXCRM_GUARDRAILS=on.\n */\n\nconst INJECTION_PATTERNS: RegExp[] = [\n /ignore\\s+(all\\s+|the\\s+|any\\s+)?(previous|above|prior|preceding)\\s+(instructions?|prompts?|messages?)/gi,\n /disregard\\s+(all\\s+|the\\s+|any\\s+)?(previous|above|prior|preceding)/gi,\n /forget\\s+(all\\s+|the\\s+|everything\\s+)?(previous|above|prior|you were told)/gi,\n /you\\s+are\\s+now\\b/gi,\n /reveal\\s+(your|the)\\s+(system\\s+)?(instructions?|prompt|prompts?)/gi,\n /(print|show|repeat)\\s+(your|the)\\s+(system\\s+)?(instructions?|prompt)/gi,\n /act\\s+as\\s+(an?\\s+)?(system|admin|administrator|developer|root)\\b/gi,\n /<\\/?(system|assistant|tool)\\b[^>]*>/gi,\n /\\bBEGIN\\s+SYSTEM\\b/gi,\n];\n\nexport function guardrailsEnabled(): boolean {\n return process.env[\"DXCRM_GUARDRAILS\"] === \"on\";\n}\n\nexport function detectPromptInjection(text: string): { flagged: boolean; matches: string[] } {\n const matches: string[] = [];\n for (const re of INJECTION_PATTERNS) {\n re.lastIndex = 0;\n const found = text.match(re);\n if (found) matches.push(...found);\n }\n return { flagged: matches.length > 0, matches };\n}\n\n/** Replace known injection spans with a [filtered] marker, keeping other text. */\nexport function neutralizeUntrusted(text: string): string {\n let out = text;\n for (const re of INJECTION_PATTERNS) {\n out = out.replace(re, \"[filtered]\");\n }\n return out;\n}\n","import { piiMaskingEnabled } from \"./pii.js\";\nimport { guardrailsEnabled } from \"./guardrails.js\";\n\n/**\n * Compliance hardening (domino D17 / §3): the cross-cutting governance layer.\n * - EU-AI-Act Art. 50 transparency: a localized disclosure that AI generated\n * the content, on by default (opt-out), wrappable around any generated text.\n * - Provider-agnostic LLM selection so the workspace can run against a local,\n * self-hosted model (Ollama / OpenAI-compatible) as a data-residency moat —\n * no customer data leaves the machine. The provider runtime itself stays in\n * the agent framework; here we only resolve and expose the configuration.\n * - A single read-out of the active privacy/compliance posture.\n */\nexport type DisclosureLang = \"de\" | \"en\";\n\nexport type LlmProviderName = \"anthropic\" | \"ollama\" | \"openai\" | \"local\";\n\nconst DISCLOSURES: Record<DisclosureLang, string> = {\n de: \"Hinweis: Dieser Inhalt wurde mithilfe von KI erstellt (EU-AI-Act Art. 50).\",\n en: \"Note: This content was generated with the help of AI (EU AI Act Art. 50).\",\n};\n\n/** Localized Art. 50 disclosure string. */\nexport function aiDisclosure(lang: DisclosureLang = \"en\"): string {\n return DISCLOSURES[lang] ?? DISCLOSURES.en;\n}\n\n/** Disclosure is ON by default (Art. 50); opt out with DXCRM_AI_DISCLOSURE=off. */\nexport function aiDisclosureEnabled(): boolean {\n return process.env[\"DXCRM_AI_DISCLOSURE\"] !== \"off\";\n}\n\n/** Prepend the Art. 50 disclosure to generated content (unless disabled). */\nexport function labelAiContent(\n text: string,\n opts: { lang?: DisclosureLang; enabled?: boolean } = {}\n): string {\n const enabled = opts.enabled ?? aiDisclosureEnabled();\n if (!enabled) return text;\n const lang = opts.lang ?? ((process.env[\"DXCRM_AI_DISCLOSURE_LANG\"] as DisclosureLang) || \"en\");\n return `${aiDisclosure(lang)}\\n\\n${text}`;\n}\n\n/** Resolve the configured LLM provider (default Anthropic). */\nexport function llmProvider(): LlmProviderName {\n const p = (process.env[\"DXCRM_LLM_PROVIDER\"] ?? \"anthropic\").toLowerCase();\n if (p === \"ollama\" || p === \"openai\" || p === \"local\") return p;\n return \"anthropic\";\n}\n\nexport interface LocalLlmConfig {\n baseUrl: string;\n model: string;\n}\n\n/** OpenAI-compatible local endpoint config (Ollama defaults), env-overridable. */\nexport function localLlmConfig(): LocalLlmConfig {\n return {\n baseUrl: process.env[\"DXCRM_LLM_BASE_URL\"] ?? \"http://127.0.0.1:11434/v1\",\n model: process.env[\"DXCRM_LLM_MODEL\"] ?? \"llama3.1\",\n };\n}\n\nexport interface ComplianceConfig {\n provider: LlmProviderName;\n local: LocalLlmConfig | null;\n aiDisclosure: boolean;\n piiMasking: boolean;\n guardrails: boolean;\n}\n\n/** A single read-out of the active privacy/compliance posture. */\nexport function complianceConfig(): ComplianceConfig {\n const provider = llmProvider();\n return {\n provider,\n local: provider === \"anthropic\" ? null : localLlmConfig(),\n aiDisclosure: aiDisclosureEnabled(),\n piiMasking: piiMaskingEnabled(),\n guardrails: guardrailsEnabled(),\n };\n}\n"],"mappings":";;;;;;;;AAOA,MAAM,WAAW;AAEjB,MAAM,WAAW;AAOjB,SAAgB,oBAA6B;CAC3C,OAAO,QAAQ,IAAI,yBAAyB;AAC9C;AAEA,SAAgB,QAAQ,OAA2B;CACjD,MAAM,0BAAU,IAAI,IAAoB;CACxC,IAAI,aAAa;CACjB,IAAI,aAAa;CAEjB,IAAI,SAAS,MAAM,QAAQ,WAAW,UAAU;EAC9C,IAAI,KAAK,QAAQ,IAAI,KAAK;EAC1B,IAAI,CAAC,IAAI;GACP,KAAK,UAAU,aAAa;GAC5B,QAAQ,IAAI,OAAO,EAAE;EACvB;EACA,OAAO;CACT,CAAC;CAED,SAAS,OAAO,QAAQ,WAAW,UAAU;EAE3C,IAAI,KAAK,QAAQ,IAAI,KAAK;EAC1B,IAAI,CAAC,IAAI;GACP,KAAK,UAAU,aAAa;GAC5B,QAAQ,IAAI,OAAO,EAAE;EACvB;EACA,OAAO;CACT,CAAC;CAED,MAAM,0BAAU,IAAI,IAAoB;CACxC,KAAK,MAAM,CAAC,MAAM,OAAO,SAAS,QAAQ,IAAI,IAAI,IAAI;CAEtD,MAAM,UAAU,SAAyB;EACvC,IAAI,MAAM;EACV,KAAK,MAAM,CAAC,IAAI,SAAS,SAAS,MAAM,IAAI,MAAM,EAAE,EAAE,KAAK,IAAI;EAC/D,OAAO;CACT;CAEA,OAAO;EAAE;EAAQ;CAAO;AAC1B;;;;;;;;AChDA,MAAM,qBAA+B;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAgB,oBAA6B;CAC3C,OAAO,QAAQ,IAAI,wBAAwB;AAC7C;;AAaA,SAAgB,oBAAoB,MAAsB;CACxD,IAAI,MAAM;CACV,KAAK,MAAM,MAAM,oBACf,MAAM,IAAI,QAAQ,IAAI,YAAY;CAEpC,OAAO;AACT;;;;;;;;;;ACtBA,MAAM,cAA8C;CAClD,IAAI;CACJ,IAAI;AACN;;AAGA,SAAgB,aAAa,OAAuB,MAAc;CAChE,OAAO,YAAY,SAAS,YAAY;AAC1C;;AAGA,SAAgB,sBAA+B;CAC7C,OAAO,QAAQ,IAAI,2BAA2B;AAChD;;AAGA,SAAgB,eACd,MACA,OAAqD,CAAC,GAC9C;CAER,IAAI,EADY,KAAK,WAAW,oBAAoB,IACtC,OAAO;CAErB,OAAO,GAAG,aADG,KAAK,SAAU,QAAQ,IAAI,+BAAkD,KAC/D,EAAE,MAAM;AACrC;;AAGA,SAAgB,cAA+B;CAC7C,MAAM,KAAK,QAAQ,IAAI,yBAAyB,aAAa,YAAY;CACzE,IAAI,MAAM,YAAY,MAAM,YAAY,MAAM,SAAS,OAAO;CAC9D,OAAO;AACT;;AAQA,SAAgB,iBAAiC;CAC/C,OAAO;EACL,SAAS,QAAQ,IAAI,yBAAyB;EAC9C,OAAO,QAAQ,IAAI,sBAAsB;CAC3C;AACF"}
|
|
@@ -84,7 +84,7 @@ const DISCLOSURES = {
|
|
|
84
84
|
en: "Note: This content was generated with the help of AI (EU AI Act Art. 50)."
|
|
85
85
|
};
|
|
86
86
|
/** Localized Art. 50 disclosure string. */
|
|
87
|
-
function aiDisclosure(lang = "
|
|
87
|
+
function aiDisclosure(lang = "en") {
|
|
88
88
|
return DISCLOSURES[lang] ?? DISCLOSURES.en;
|
|
89
89
|
}
|
|
90
90
|
/** Disclosure is ON by default (Art. 50); opt out with DXCRM_AI_DISCLOSURE=off. */
|
|
@@ -94,7 +94,7 @@ function aiDisclosureEnabled() {
|
|
|
94
94
|
/** Prepend the Art. 50 disclosure to generated content (unless disabled). */
|
|
95
95
|
function labelAiContent(text, opts = {}) {
|
|
96
96
|
if (!(opts.enabled ?? aiDisclosureEnabled())) return text;
|
|
97
|
-
return `${aiDisclosure(opts.lang ?? (process.env["DXCRM_AI_DISCLOSURE_LANG"] || "
|
|
97
|
+
return `${aiDisclosure(opts.lang ?? (process.env["DXCRM_AI_DISCLOSURE_LANG"] || "en"))}\n\n${text}`;
|
|
98
98
|
}
|
|
99
99
|
/** Resolve the configured LLM provider (default Anthropic). */
|
|
100
100
|
function llmProvider() {
|
|
@@ -153,4 +153,4 @@ Object.defineProperty(exports, "piiMaskingEnabled", {
|
|
|
153
153
|
}
|
|
154
154
|
});
|
|
155
155
|
|
|
156
|
-
//# sourceMappingURL=compliance-
|
|
156
|
+
//# sourceMappingURL=compliance-pAj9FcGI.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compliance-
|
|
1
|
+
{"version":3,"file":"compliance-pAj9FcGI.cjs","names":[],"sources":["../src/core/pii.ts","../src/core/guardrails.ts","../src/core/compliance.ts"],"sourcesContent":["/**\n * Lightweight PII masking for LLM calls (Einstein-Trust-Layer-style).\n * Replaces emails and phone numbers with stable placeholders before text is\n * sent to an LLM, and can restore them in the response. Opt-in via\n * DXCRM_PII_MASKING=on so default behaviour and quality are unchanged.\n */\n\nconst EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g;\n// Phone: optional +, then 7+ digits possibly grouped by spaces/dashes/parens.\nconst PHONE_RE = /\\+?\\d[\\d\\s().-]{6,}\\d/g;\n\nexport interface MaskedText {\n masked: string;\n unmask: (text: string) => string;\n}\n\nexport function piiMaskingEnabled(): boolean {\n return process.env[\"DXCRM_PII_MASKING\"] === \"on\";\n}\n\nexport function maskPii(input: string): MaskedText {\n const mapping = new Map<string, string>(); // original -> placeholder\n let emailCount = 0;\n let phoneCount = 0;\n\n let masked = input.replace(EMAIL_RE, (match) => {\n let ph = mapping.get(match);\n if (!ph) {\n ph = `[EMAIL_${emailCount++}]`;\n mapping.set(match, ph);\n }\n return ph;\n });\n\n masked = masked.replace(PHONE_RE, (match) => {\n // Ignore very short numeric runs that slipped through (already handled by {6,}).\n let ph = mapping.get(match);\n if (!ph) {\n ph = `[PHONE_${phoneCount++}]`;\n mapping.set(match, ph);\n }\n return ph;\n });\n\n const reverse = new Map<string, string>(); // placeholder -> original\n for (const [orig, ph] of mapping) reverse.set(ph, orig);\n\n const unmask = (text: string): string => {\n let out = text;\n for (const [ph, orig] of reverse) out = out.split(ph).join(orig);\n return out;\n };\n\n return { masked, unmask };\n}\n","/**\n * Guardrails against (indirect) prompt injection in untrusted CRM content\n * (emails, transcripts, imported notes). Detection is always available;\n * neutralization is applied to LLM inputs when DXCRM_GUARDRAILS=on.\n */\n\nconst INJECTION_PATTERNS: RegExp[] = [\n /ignore\\s+(all\\s+|the\\s+|any\\s+)?(previous|above|prior|preceding)\\s+(instructions?|prompts?|messages?)/gi,\n /disregard\\s+(all\\s+|the\\s+|any\\s+)?(previous|above|prior|preceding)/gi,\n /forget\\s+(all\\s+|the\\s+|everything\\s+)?(previous|above|prior|you were told)/gi,\n /you\\s+are\\s+now\\b/gi,\n /reveal\\s+(your|the)\\s+(system\\s+)?(instructions?|prompt|prompts?)/gi,\n /(print|show|repeat)\\s+(your|the)\\s+(system\\s+)?(instructions?|prompt)/gi,\n /act\\s+as\\s+(an?\\s+)?(system|admin|administrator|developer|root)\\b/gi,\n /<\\/?(system|assistant|tool)\\b[^>]*>/gi,\n /\\bBEGIN\\s+SYSTEM\\b/gi,\n];\n\nexport function guardrailsEnabled(): boolean {\n return process.env[\"DXCRM_GUARDRAILS\"] === \"on\";\n}\n\nexport function detectPromptInjection(text: string): { flagged: boolean; matches: string[] } {\n const matches: string[] = [];\n for (const re of INJECTION_PATTERNS) {\n re.lastIndex = 0;\n const found = text.match(re);\n if (found) matches.push(...found);\n }\n return { flagged: matches.length > 0, matches };\n}\n\n/** Replace known injection spans with a [filtered] marker, keeping other text. */\nexport function neutralizeUntrusted(text: string): string {\n let out = text;\n for (const re of INJECTION_PATTERNS) {\n out = out.replace(re, \"[filtered]\");\n }\n return out;\n}\n","import { piiMaskingEnabled } from \"./pii.js\";\nimport { guardrailsEnabled } from \"./guardrails.js\";\n\n/**\n * Compliance hardening (domino D17 / §3): the cross-cutting governance layer.\n * - EU-AI-Act Art. 50 transparency: a localized disclosure that AI generated\n * the content, on by default (opt-out), wrappable around any generated text.\n * - Provider-agnostic LLM selection so the workspace can run against a local,\n * self-hosted model (Ollama / OpenAI-compatible) as a data-residency moat —\n * no customer data leaves the machine. The provider runtime itself stays in\n * the agent framework; here we only resolve and expose the configuration.\n * - A single read-out of the active privacy/compliance posture.\n */\nexport type DisclosureLang = \"de\" | \"en\";\n\nexport type LlmProviderName = \"anthropic\" | \"ollama\" | \"openai\" | \"local\";\n\nconst DISCLOSURES: Record<DisclosureLang, string> = {\n de: \"Hinweis: Dieser Inhalt wurde mithilfe von KI erstellt (EU-AI-Act Art. 50).\",\n en: \"Note: This content was generated with the help of AI (EU AI Act Art. 50).\",\n};\n\n/** Localized Art. 50 disclosure string. */\nexport function aiDisclosure(lang: DisclosureLang = \"en\"): string {\n return DISCLOSURES[lang] ?? DISCLOSURES.en;\n}\n\n/** Disclosure is ON by default (Art. 50); opt out with DXCRM_AI_DISCLOSURE=off. */\nexport function aiDisclosureEnabled(): boolean {\n return process.env[\"DXCRM_AI_DISCLOSURE\"] !== \"off\";\n}\n\n/** Prepend the Art. 50 disclosure to generated content (unless disabled). */\nexport function labelAiContent(\n text: string,\n opts: { lang?: DisclosureLang; enabled?: boolean } = {}\n): string {\n const enabled = opts.enabled ?? aiDisclosureEnabled();\n if (!enabled) return text;\n const lang = opts.lang ?? ((process.env[\"DXCRM_AI_DISCLOSURE_LANG\"] as DisclosureLang) || \"en\");\n return `${aiDisclosure(lang)}\\n\\n${text}`;\n}\n\n/** Resolve the configured LLM provider (default Anthropic). */\nexport function llmProvider(): LlmProviderName {\n const p = (process.env[\"DXCRM_LLM_PROVIDER\"] ?? \"anthropic\").toLowerCase();\n if (p === \"ollama\" || p === \"openai\" || p === \"local\") return p;\n return \"anthropic\";\n}\n\nexport interface LocalLlmConfig {\n baseUrl: string;\n model: string;\n}\n\n/** OpenAI-compatible local endpoint config (Ollama defaults), env-overridable. */\nexport function localLlmConfig(): LocalLlmConfig {\n return {\n baseUrl: process.env[\"DXCRM_LLM_BASE_URL\"] ?? \"http://127.0.0.1:11434/v1\",\n model: process.env[\"DXCRM_LLM_MODEL\"] ?? \"llama3.1\",\n };\n}\n\nexport interface ComplianceConfig {\n provider: LlmProviderName;\n local: LocalLlmConfig | null;\n aiDisclosure: boolean;\n piiMasking: boolean;\n guardrails: boolean;\n}\n\n/** A single read-out of the active privacy/compliance posture. */\nexport function complianceConfig(): ComplianceConfig {\n const provider = llmProvider();\n return {\n provider,\n local: provider === \"anthropic\" ? null : localLlmConfig(),\n aiDisclosure: aiDisclosureEnabled(),\n piiMasking: piiMaskingEnabled(),\n guardrails: guardrailsEnabled(),\n };\n}\n"],"mappings":";;;;;;;;AAOA,MAAM,WAAW;AAEjB,MAAM,WAAW;AAOjB,SAAgB,oBAA6B;CAC3C,OAAO,QAAQ,IAAI,yBAAyB;AAC9C;AAEA,SAAgB,QAAQ,OAA2B;CACjD,MAAM,0BAAU,IAAI,IAAoB;CACxC,IAAI,aAAa;CACjB,IAAI,aAAa;CAEjB,IAAI,SAAS,MAAM,QAAQ,WAAW,UAAU;EAC9C,IAAI,KAAK,QAAQ,IAAI,KAAK;EAC1B,IAAI,CAAC,IAAI;GACP,KAAK,UAAU,aAAa;GAC5B,QAAQ,IAAI,OAAO,EAAE;EACvB;EACA,OAAO;CACT,CAAC;CAED,SAAS,OAAO,QAAQ,WAAW,UAAU;EAE3C,IAAI,KAAK,QAAQ,IAAI,KAAK;EAC1B,IAAI,CAAC,IAAI;GACP,KAAK,UAAU,aAAa;GAC5B,QAAQ,IAAI,OAAO,EAAE;EACvB;EACA,OAAO;CACT,CAAC;CAED,MAAM,0BAAU,IAAI,IAAoB;CACxC,KAAK,MAAM,CAAC,MAAM,OAAO,SAAS,QAAQ,IAAI,IAAI,IAAI;CAEtD,MAAM,UAAU,SAAyB;EACvC,IAAI,MAAM;EACV,KAAK,MAAM,CAAC,IAAI,SAAS,SAAS,MAAM,IAAI,MAAM,EAAE,EAAE,KAAK,IAAI;EAC/D,OAAO;CACT;CAEA,OAAO;EAAE;EAAQ;CAAO;AAC1B;;;;;;;;AChDA,MAAM,qBAA+B;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAgB,oBAA6B;CAC3C,OAAO,QAAQ,IAAI,wBAAwB;AAC7C;;AAaA,SAAgB,oBAAoB,MAAsB;CACxD,IAAI,MAAM;CACV,KAAK,MAAM,MAAM,oBACf,MAAM,IAAI,QAAQ,IAAI,YAAY;CAEpC,OAAO;AACT;;;;;;;;;;ACtBA,MAAM,cAA8C;CAClD,IAAI;CACJ,IAAI;AACN;;AAGA,SAAgB,aAAa,OAAuB,MAAc;CAChE,OAAO,YAAY,SAAS,YAAY;AAC1C;;AAGA,SAAgB,sBAA+B;CAC7C,OAAO,QAAQ,IAAI,2BAA2B;AAChD;;AAGA,SAAgB,eACd,MACA,OAAqD,CAAC,GAC9C;CAER,IAAI,EADY,KAAK,WAAW,oBAAoB,IACtC,OAAO;CAErB,OAAO,GAAG,aADG,KAAK,SAAU,QAAQ,IAAI,+BAAkD,KAC/D,EAAE,MAAM;AACrC;;AAGA,SAAgB,cAA+B;CAC7C,MAAM,KAAK,QAAQ,IAAI,yBAAyB,aAAa,YAAY;CACzE,IAAI,MAAM,YAAY,MAAM,YAAY,MAAM,SAAS,OAAO;CAC9D,OAAO;AACT;;AAQA,SAAgB,iBAAiC;CAC/C,OAAO;EACL,SAAS,QAAQ,IAAI,yBAAyB;EAC9C,OAAO,QAAQ,IAAI,sBAAsB;CAC3C;AACF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as escapeRegExp } from "./regex-Jt5DatPi.js";
|
|
1
2
|
import path from "path";
|
|
2
3
|
import fs from "fs";
|
|
3
4
|
import matter from "gray-matter";
|
|
@@ -15,7 +16,7 @@ function parsePipelineContent(filePath) {
|
|
|
15
16
|
return fs.readFileSync(filePath, "utf-8").trim();
|
|
16
17
|
}
|
|
17
18
|
function extractSection(content, sectionName) {
|
|
18
|
-
const match = new RegExp(`## ${sectionName}([\\s\\S]*?)(?=^## |$)`, "m").exec(content);
|
|
19
|
+
const match = new RegExp(`## ${escapeRegExp(sectionName)}([\\s\\S]*?)(?=^## |$)`, "m").exec(content);
|
|
19
20
|
return match ? (match[1] ?? "").trim() : "";
|
|
20
21
|
}
|
|
21
22
|
async function buildContext(dataDir, slug) {
|
|
@@ -93,4 +94,4 @@ async function buildContext(dataDir, slug) {
|
|
|
93
94
|
//#endregion
|
|
94
95
|
export { buildContext as t };
|
|
95
96
|
|
|
96
|
-
//# sourceMappingURL=context-builder-
|
|
97
|
+
//# sourceMappingURL=context-builder-7Uab5-G4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-builder-7Uab5-G4.js","names":[],"sources":["../src/core/context-builder.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { escapeRegExp } from \"./regex.js\";\n\nconst MAX_INTERACTIONS = 10;\n\nfunction estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\nfunction parseRecentInteractions(filePath: string, limit: number): string {\n if (!fs.existsSync(filePath)) return \"\";\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n\n // Split on ## date headings\n const entries = content.split(/(?=^## \\d{4}-\\d{2}-\\d{2})/m).filter((e) => e.trim());\n const recent = entries.slice(0, limit);\n return recent.join(\"\\n\").trim();\n}\n\nfunction parsePipelineContent(filePath: string): string {\n if (!fs.existsSync(filePath)) return \"\";\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n return content.trim();\n}\n\nfunction extractSection(content: string, sectionName: string): string {\n const regex = new RegExp(`## ${escapeRegExp(sectionName)}([\\\\s\\\\S]*?)(?=^## |$)`, \"m\");\n const match = regex.exec(content);\n return match ? (match[1] ?? \"\").trim() : \"\";\n}\n\nexport async function buildContext(dataDir: string, slug: string): Promise<string> {\n const customerDir = path.join(dataDir, \"customers\", slug);\n\n if (!fs.existsSync(customerDir)) {\n throw new Error(`Customer '${slug}' not found`);\n }\n\n const mainFactsPath = path.join(customerDir, \"main_facts.md\");\n const interactionsPath = path.join(customerDir, \"interactions.md\");\n const pipelinePath = path.join(customerDir, \"pipeline.md\");\n\n // Read main_facts.md\n let mainContent = \"\";\n let frontmatterStr = \"\";\n if (fs.existsSync(mainFactsPath)) {\n const fileContent = fs.readFileSync(mainFactsPath, \"utf-8\") as string;\n const raw = matter(fileContent);\n mainContent = raw.content ?? \"\";\n frontmatterStr = Object.entries(raw.data as Record<string, unknown>)\n .map(([k, v]) => `${k}: ${JSON.stringify(v)}`)\n .join(\"\\n\");\n }\n\n const quickRef = extractSection(mainContent, \"Quick Reference\");\n const contacts = extractSection(mainContent, \"Contacts\");\n const criticalContext = extractSection(mainContent, \"Critical Context\");\n const openQuestions = extractSection(mainContent, \"Open Questions\");\n\n const recentActivity = parseRecentInteractions(interactionsPath, MAX_INTERACTIONS);\n const pipelineContent = parsePipelineContent(pipelinePath);\n\n const sections: string[] = [\n `# Customer Context: ${slug}`,\n \"\",\n \"## Metadata\",\n frontmatterStr || \"(no metadata)\",\n \"\",\n \"## Quick Reference\",\n quickRef || \"(not set)\",\n \"\",\n \"## Contacts\",\n contacts || \"(not set)\",\n \"\",\n \"## Critical Context\",\n criticalContext || \"(not set)\",\n \"\",\n \"## Recent Activity (last 10 interactions)\",\n recentActivity || \"(no interactions yet)\",\n \"\",\n \"## Pipeline\",\n pipelineContent || \"(no deals)\",\n \"\",\n \"## Open Questions\",\n openQuestions || \"(none)\",\n ];\n\n const raw = sections.join(\"\\n\");\n const tokenEstimate = estimateTokens(raw);\n\n // If over 3000 tokens, trim interactions\n if (tokenEstimate > 3000) {\n const trimmedActivity = parseRecentInteractions(interactionsPath, 5);\n const trimmedSections: string[] = [\n `# Customer Context: ${slug}`,\n \"\",\n \"## Metadata\",\n frontmatterStr || \"(no metadata)\",\n \"\",\n \"## Quick Reference\",\n quickRef || \"(not set)\",\n \"\",\n \"## Contacts\",\n contacts || \"(not set)\",\n \"\",\n \"## Critical Context\",\n criticalContext || \"(not set)\",\n \"\",\n \"## Recent Activity (last 5 interactions — trimmed for token budget)\",\n trimmedActivity || \"(no interactions yet)\",\n \"\",\n \"## Pipeline\",\n pipelineContent || \"(no deals)\",\n \"\",\n \"## Open Questions\",\n openQuestions || \"(none)\",\n ];\n return trimmedSections.join(\"\\n\");\n }\n\n return raw;\n}\n\n/** Robust section-body extractor: from a `## Name` heading to the next `## ` heading. */\nfunction sectionBody(content: string, name: string): string {\n const lines = content.split(\"\\n\");\n const start = lines.findIndex((l) => l.trim() === `## ${name}`);\n if (start < 0) return \"\";\n const body: string[] = [];\n for (let i = start + 1; i < lines.length; i++) {\n if (lines[i]!.startsWith(\"## \")) break;\n body.push(lines[i]!);\n }\n return body.join(\"\\n\").trim();\n}\n\nexport interface ContextBlock {\n slug: string;\n metadata: Record<string, unknown>;\n quickReference: string;\n contacts: string;\n criticalContext: string;\n openQuestions: string;\n recentActivity: string;\n pipeline: string;\n}\n\n/**\n * Structured variant of buildContext (REF-2): returns a typed object instead of\n * a markdown string, for callers that need fields programmatically (e.g. MCP\n * responses, SDK consumers). buildContext remains the token-budgeted string form.\n */\nexport async function buildContextBlock(\n dataDir: string,\n slug: string,\n role?: \"admin\" | \"manager\" | \"rep\"\n): Promise<ContextBlock> {\n const customerDir = path.join(dataDir, \"customers\", slug);\n if (!fs.existsSync(customerDir)) {\n throw new Error(`Customer '${slug}' not found`);\n }\n\n const mainFactsPath = path.join(customerDir, \"main_facts.md\");\n const interactionsPath = path.join(customerDir, \"interactions.md\");\n const pipelinePath = path.join(customerDir, \"pipeline.md\");\n\n let mainContent = \"\";\n let metadata: Record<string, unknown> = {};\n if (fs.existsSync(mainFactsPath)) {\n const raw = matter(fs.readFileSync(mainFactsPath, \"utf-8\") as string);\n mainContent = raw.content ?? \"\";\n metadata = raw.data as Record<string, unknown>;\n }\n\n // Field-level security: redact metadata fields the role may not see.\n if (role) {\n const { loadFieldAcl, redactFields } = await import(\"./rbac.js\");\n metadata = redactFields(metadata, role, loadFieldAcl(dataDir));\n }\n\n return {\n slug,\n metadata,\n quickReference: sectionBody(mainContent, \"Quick Reference\"),\n contacts: sectionBody(mainContent, \"Contacts\"),\n criticalContext: sectionBody(mainContent, \"Critical Context\"),\n openQuestions: sectionBody(mainContent, \"Open Questions\"),\n recentActivity: parseRecentInteractions(interactionsPath, MAX_INTERACTIONS),\n pipeline: parsePipelineContent(pipelinePath),\n };\n}\n"],"mappings":";;;;;AAKA,MAAM,mBAAmB;AAEzB,SAAS,eAAe,MAAsB;CAC5C,OAAO,KAAK,KAAK,KAAK,SAAS,CAAC;AAClC;AAEA,SAAS,wBAAwB,UAAkB,OAAuB;CACxE,IAAI,CAAC,GAAG,WAAW,QAAQ,GAAG,OAAO;CAMrC,OALgB,GAAG,aAAa,UAAU,OAGpB,EAAE,MAAM,4BAA4B,EAAE,QAAQ,MAAM,EAAE,KAAK,CAC5D,EAAE,MAAM,GAAG,KACpB,EAAE,KAAK,IAAI,EAAE,KAAK;AAChC;AAEA,SAAS,qBAAqB,UAA0B;CACtD,IAAI,CAAC,GAAG,WAAW,QAAQ,GAAG,OAAO;CAErC,OADgB,GAAG,aAAa,UAAU,OAC7B,EAAE,KAAK;AACtB;AAEA,SAAS,eAAe,SAAiB,aAA6B;CAEpE,MAAM,QAAQ,IADI,OAAO,MAAM,aAAa,WAAW,EAAE,yBAAyB,GAChE,EAAE,KAAK,OAAO;CAChC,OAAO,SAAS,MAAM,MAAM,IAAI,KAAK,IAAI;AAC3C;AAEA,eAAsB,aAAa,SAAiB,MAA+B;CACjF,MAAM,cAAc,KAAK,KAAK,SAAS,aAAa,IAAI;CAExD,IAAI,CAAC,GAAG,WAAW,WAAW,GAC5B,MAAM,IAAI,MAAM,aAAa,KAAK,YAAY;CAGhD,MAAM,gBAAgB,KAAK,KAAK,aAAa,eAAe;CAC5D,MAAM,mBAAmB,KAAK,KAAK,aAAa,iBAAiB;CACjE,MAAM,eAAe,KAAK,KAAK,aAAa,aAAa;CAGzD,IAAI,cAAc;CAClB,IAAI,iBAAiB;CACrB,IAAI,GAAG,WAAW,aAAa,GAAG;EAEhC,MAAM,MAAM,OADQ,GAAG,aAAa,eAAe,OACtB,CAAC;EAC9B,cAAc,IAAI,WAAW;EAC7B,iBAAiB,OAAO,QAAQ,IAAI,IAA+B,EAChE,KAAK,CAAC,GAAG,OAAO,GAAG,EAAE,IAAI,KAAK,UAAU,CAAC,GAAG,EAC5C,KAAK,IAAI;CACd;CAEA,MAAM,WAAW,eAAe,aAAa,iBAAiB;CAC9D,MAAM,WAAW,eAAe,aAAa,UAAU;CACvD,MAAM,kBAAkB,eAAe,aAAa,kBAAkB;CACtE,MAAM,gBAAgB,eAAe,aAAa,gBAAgB;CAElE,MAAM,iBAAiB,wBAAwB,kBAAkB,gBAAgB;CACjF,MAAM,kBAAkB,qBAAqB,YAAY;CA2BzD,MAAM,MAAM;EAxBV,uBAAuB;EACvB;EACA;EACA,kBAAkB;EAClB;EACA;EACA,YAAY;EACZ;EACA;EACA,YAAY;EACZ;EACA;EACA,mBAAmB;EACnB;EACA;EACA,kBAAkB;EAClB;EACA;EACA,mBAAmB;EACnB;EACA;EACA,iBAAiB;CAGA,EAAE,KAAK,IAAI;CAI9B,IAHsB,eAAe,GAGrB,IAAI,KAAM;EACxB,MAAM,kBAAkB,wBAAwB,kBAAkB,CAAC;EAyBnE,OAAO;GAvBL,uBAAuB;GACvB;GACA;GACA,kBAAkB;GAClB;GACA;GACA,YAAY;GACZ;GACA;GACA,YAAY;GACZ;GACA;GACA,mBAAmB;GACnB;GACA;GACA,mBAAmB;GACnB;GACA;GACA,mBAAmB;GACnB;GACA;GACA,iBAAiB;EAEE,EAAE,KAAK,IAAI;CAClC;CAEA,OAAO;AACT"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as loadFieldDefinitions, t as defineCustomField } from "./custom-fields-
|
|
1
|
+
import { n as loadFieldDefinitions, t as defineCustomField } from "./custom-fields-GzpOHW_2.js";
|
|
2
2
|
export { defineCustomField, loadFieldDefinitions };
|
|
@@ -1,18 +1,11 @@
|
|
|
1
|
+
import { r as writeJsonArray, t as readJsonArray } from "./json-store-WWsFzXub.js";
|
|
1
2
|
import path from "path";
|
|
2
|
-
import fs from "fs";
|
|
3
3
|
//#region src/core/custom-fields.ts
|
|
4
4
|
function schemaPath(dataDir) {
|
|
5
5
|
return path.join(dataDir, ".agentic", "schema", "custom-fields.json");
|
|
6
6
|
}
|
|
7
7
|
function loadFieldDefinitions(dataDir) {
|
|
8
|
-
|
|
9
|
-
if (!fs.existsSync(p)) return [];
|
|
10
|
-
try {
|
|
11
|
-
const data = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
12
|
-
return Array.isArray(data.fields) ? data.fields : [];
|
|
13
|
-
} catch {
|
|
14
|
-
return [];
|
|
15
|
-
}
|
|
8
|
+
return readJsonArray(schemaPath(dataDir), "fields");
|
|
16
9
|
}
|
|
17
10
|
/** Add or update (by name) a custom field definition. */
|
|
18
11
|
function defineCustomField(dataDir, def) {
|
|
@@ -20,9 +13,7 @@ function defineCustomField(dataDir, def) {
|
|
|
20
13
|
const idx = defs.findIndex((d) => d.name === def.name);
|
|
21
14
|
if (idx >= 0) defs[idx] = def;
|
|
22
15
|
else defs.push(def);
|
|
23
|
-
|
|
24
|
-
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
25
|
-
fs.writeFileSync(p, JSON.stringify({ fields: defs }, null, 2), "utf-8");
|
|
16
|
+
writeJsonArray(schemaPath(dataDir), "fields", defs);
|
|
26
17
|
return defs;
|
|
27
18
|
}
|
|
28
19
|
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
@@ -70,4 +61,4 @@ function validateCustomFields(input, defs) {
|
|
|
70
61
|
//#endregion
|
|
71
62
|
export { loadFieldDefinitions as n, validateCustomFields as r, defineCustomField as t };
|
|
72
63
|
|
|
73
|
-
//# sourceMappingURL=custom-fields-
|
|
64
|
+
//# sourceMappingURL=custom-fields-GzpOHW_2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-fields-GzpOHW_2.js","names":[],"sources":["../src/core/custom-fields.ts"],"sourcesContent":["import path from \"path\";\nimport { readJsonArray, writeJsonArray } from \"../fs/json-store.js\";\n\n/**\n * Metadata-driven custom fields — the first increment of the metadata model\n * (next-plan N1-7). Definitions live in .agentic/schema/custom-fields.json and\n * extend customers without code changes. Core schemas stay strict; custom\n * fields are validated separately against this registry.\n */\nexport type CustomFieldType = \"text\" | \"number\" | \"boolean\" | \"date\" | \"select\";\n\nexport interface FieldDefinition {\n name: string;\n type: CustomFieldType;\n label?: string;\n options?: string[];\n}\n\nfunction schemaPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"schema\", \"custom-fields.json\");\n}\n\nexport function loadFieldDefinitions(dataDir: string): FieldDefinition[] {\n return readJsonArray<FieldDefinition>(schemaPath(dataDir), \"fields\");\n}\n\n/** Add or update (by name) a custom field definition. */\nexport function defineCustomField(dataDir: string, def: FieldDefinition): FieldDefinition[] {\n const defs = loadFieldDefinitions(dataDir);\n const idx = defs.findIndex((d) => d.name === def.name);\n if (idx >= 0) defs[idx] = def;\n else defs.push(def);\n writeJsonArray(schemaPath(dataDir), \"fields\", defs);\n return defs;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n values: Record<string, string | number | boolean>;\n errors: string[];\n}\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** Validate + coerce a record of raw values against custom field definitions. */\nexport function validateCustomFields(\n input: Record<string, unknown>,\n defs: FieldDefinition[]\n): ValidationResult {\n const byName = new Map(defs.map((d) => [d.name, d]));\n const values: Record<string, string | number | boolean> = {};\n const errors: string[] = [];\n\n for (const [key, raw] of Object.entries(input)) {\n const def = byName.get(key);\n if (!def) {\n errors.push(`Unknown custom field: ${key}`);\n continue;\n }\n const str = String(raw).trim();\n switch (def.type) {\n case \"number\": {\n const n = Number(str);\n if (!Number.isFinite(n)) errors.push(`${key}: not a number`);\n else values[key] = n;\n break;\n }\n case \"boolean\": {\n if (/^(true|yes|1)$/i.test(str)) values[key] = true;\n else if (/^(false|no|0)$/i.test(str)) values[key] = false;\n else errors.push(`${key}: not a boolean`);\n break;\n }\n case \"date\": {\n if (DATE_RE.test(str)) values[key] = str;\n else errors.push(`${key}: expected YYYY-MM-DD`);\n break;\n }\n case \"select\": {\n if (def.options && def.options.includes(str)) values[key] = str;\n else errors.push(`${key}: must be one of ${(def.options ?? []).join(\", \")}`);\n break;\n }\n default:\n values[key] = str;\n }\n }\n\n return { valid: errors.length === 0, values, errors };\n}\n"],"mappings":";;;AAkBA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAK,KAAK,SAAS,YAAY,UAAU,oBAAoB;AACtE;AAEA,SAAgB,qBAAqB,SAAoC;CACvE,OAAO,cAA+B,WAAW,OAAO,GAAG,QAAQ;AACrE;;AAGA,SAAgB,kBAAkB,SAAiB,KAAyC;CAC1F,MAAM,OAAO,qBAAqB,OAAO;CACzC,MAAM,MAAM,KAAK,WAAW,MAAM,EAAE,SAAS,IAAI,IAAI;CACrD,IAAI,OAAO,GAAG,KAAK,OAAO;MACrB,KAAK,KAAK,GAAG;CAClB,eAAe,WAAW,OAAO,GAAG,UAAU,IAAI;CAClD,OAAO;AACT;AAQA,MAAM,UAAU;;AAGhB,SAAgB,qBACd,OACA,MACkB;CAClB,MAAM,SAAS,IAAI,IAAI,KAAK,KAAK,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD,MAAM,SAAoD,CAAC;CAC3D,MAAM,SAAmB,CAAC;CAE1B,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,KAAK,GAAG;EAC9C,MAAM,MAAM,OAAO,IAAI,GAAG;EAC1B,IAAI,CAAC,KAAK;GACR,OAAO,KAAK,yBAAyB,KAAK;GAC1C;EACF;EACA,MAAM,MAAM,OAAO,GAAG,EAAE,KAAK;EAC7B,QAAQ,IAAI,MAAZ;GACE,KAAK,UAAU;IACb,MAAM,IAAI,OAAO,GAAG;IACpB,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO,KAAK,GAAG,IAAI,eAAe;SACtD,OAAO,OAAO;IACnB;GACF;GACA,KAAK;IACH,IAAI,kBAAkB,KAAK,GAAG,GAAG,OAAO,OAAO;SAC1C,IAAI,kBAAkB,KAAK,GAAG,GAAG,OAAO,OAAO;SAC/C,OAAO,KAAK,GAAG,IAAI,gBAAgB;IACxC;GAEF,KAAK;IACH,IAAI,QAAQ,KAAK,GAAG,GAAG,OAAO,OAAO;SAChC,OAAO,KAAK,GAAG,IAAI,sBAAsB;IAC9C;GAEF,KAAK;IACH,IAAI,IAAI,WAAW,IAAI,QAAQ,SAAS,GAAG,GAAG,OAAO,OAAO;SACvD,OAAO,KAAK,GAAG,IAAI,oBAAoB,IAAI,WAAW,CAAC,GAAG,KAAK,IAAI,GAAG;IAC3E;GAEF,SACE,OAAO,OAAO;EAClB;CACF;CAEA,OAAO;EAAE,OAAO,OAAO,WAAW;EAAG;EAAQ;CAAO;AACtD"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as listRecords, n as defineCustomObject, t as createRecord } from "./custom-objects-
|
|
1
|
+
import { i as listRecords, n as defineCustomObject, t as createRecord } from "./custom-objects-CxW1gHwJ.js";
|
|
2
2
|
export { createRecord, defineCustomObject, listRecords };
|
|
@@ -1,52 +1,37 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as assertSafePathSegment } from "./safe-path-mpp0dKtO.js";
|
|
2
|
+
import { r as writeJsonArray, t as readJsonArray } from "./json-store-WWsFzXub.js";
|
|
3
|
+
import { r as validateCustomFields } from "./custom-fields-GzpOHW_2.js";
|
|
2
4
|
import path from "path";
|
|
3
|
-
import fs from "fs";
|
|
4
5
|
import { randomBytes } from "crypto";
|
|
5
6
|
//#region src/core/custom-objects.ts
|
|
6
7
|
function objectsSchemaPath(dataDir) {
|
|
7
8
|
return path.join(dataDir, ".agentic", "schema", "custom-objects.json");
|
|
8
9
|
}
|
|
9
10
|
function recordsPath(dataDir, name) {
|
|
11
|
+
assertSafePathSegment(name, "custom object name");
|
|
10
12
|
return path.join(dataDir, ".agentic", "objects", `${name}.json`);
|
|
11
13
|
}
|
|
12
14
|
function loadCustomObjects(dataDir) {
|
|
13
|
-
|
|
14
|
-
if (!fs.existsSync(p)) return [];
|
|
15
|
-
try {
|
|
16
|
-
const data = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
17
|
-
return Array.isArray(data.objects) ? data.objects : [];
|
|
18
|
-
} catch {
|
|
19
|
-
return [];
|
|
20
|
-
}
|
|
15
|
+
return readJsonArray(objectsSchemaPath(dataDir), "objects");
|
|
21
16
|
}
|
|
22
17
|
function getObjectDefinition(dataDir, name) {
|
|
23
18
|
return loadCustomObjects(dataDir).find((o) => o.name === name);
|
|
24
19
|
}
|
|
25
20
|
/** Add or update (by name) a custom object definition. */
|
|
26
21
|
function defineCustomObject(dataDir, def) {
|
|
22
|
+
assertSafePathSegment(def.name, "custom object name");
|
|
27
23
|
const objs = loadCustomObjects(dataDir);
|
|
28
24
|
const idx = objs.findIndex((o) => o.name === def.name);
|
|
29
25
|
if (idx >= 0) objs[idx] = def;
|
|
30
26
|
else objs.push(def);
|
|
31
|
-
|
|
32
|
-
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
33
|
-
fs.writeFileSync(p, JSON.stringify({ objects: objs }, null, 2), "utf-8");
|
|
27
|
+
writeJsonArray(objectsSchemaPath(dataDir), "objects", objs);
|
|
34
28
|
return objs;
|
|
35
29
|
}
|
|
36
30
|
function listRecords(dataDir, name) {
|
|
37
|
-
|
|
38
|
-
if (!fs.existsSync(p)) return [];
|
|
39
|
-
try {
|
|
40
|
-
const data = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
41
|
-
return Array.isArray(data.records) ? data.records : [];
|
|
42
|
-
} catch {
|
|
43
|
-
return [];
|
|
44
|
-
}
|
|
31
|
+
return readJsonArray(recordsPath(dataDir, name), "records");
|
|
45
32
|
}
|
|
46
33
|
function writeRecords(dataDir, name, records) {
|
|
47
|
-
|
|
48
|
-
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
49
|
-
fs.writeFileSync(p, JSON.stringify({ records }, null, 2), "utf-8");
|
|
34
|
+
writeJsonArray(recordsPath(dataDir, name), "records", records);
|
|
50
35
|
}
|
|
51
36
|
function createRecord(dataDir, name, values) {
|
|
52
37
|
const def = getObjectDefinition(dataDir, name);
|
|
@@ -75,4 +60,4 @@ function createRecord(dataDir, name, values) {
|
|
|
75
60
|
//#endregion
|
|
76
61
|
export { loadCustomObjects as a, listRecords as i, defineCustomObject as n, getObjectDefinition as r, createRecord as t };
|
|
77
62
|
|
|
78
|
-
//# sourceMappingURL=custom-objects-
|
|
63
|
+
//# sourceMappingURL=custom-objects-CxW1gHwJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-objects-CxW1gHwJ.js","names":[],"sources":["../src/core/custom-objects.ts"],"sourcesContent":["import { randomBytes } from \"crypto\";\nimport path from \"path\";\nimport { validateCustomFields, type FieldDefinition } from \"./custom-fields.js\";\nimport { readJsonArray, writeJsonArray } from \"../fs/json-store.js\";\nimport { assertSafePathSegment } from \"../fs/safe-path.js\";\n\n/**\n * Custom objects — runtime-defined entity types with their own fields, stored\n * as JSON without code migrations (Twenty-style \"no-migration\" model, the\n * N5-1 increment of the metadata layer). Definitions live in\n * .agentic/schema/custom-objects.json; records in .agentic/objects/<name>.json.\n */\nexport interface ObjectDefinition {\n name: string;\n label?: string;\n fields: FieldDefinition[];\n}\n\nexport interface ObjectRecord {\n id: string;\n createdAt: string;\n updatedAt: string;\n values: Record<string, string | number | boolean>;\n}\n\nexport interface RecordResult {\n ok: boolean;\n record?: ObjectRecord;\n errors?: string[];\n}\n\nfunction objectsSchemaPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"schema\", \"custom-objects.json\");\n}\nfunction recordsPath(dataDir: string, name: string): string {\n assertSafePathSegment(name, \"custom object name\");\n return path.join(dataDir, \".agentic\", \"objects\", `${name}.json`);\n}\n\nexport function loadCustomObjects(dataDir: string): ObjectDefinition[] {\n return readJsonArray<ObjectDefinition>(objectsSchemaPath(dataDir), \"objects\");\n}\n\nexport function getObjectDefinition(dataDir: string, name: string): ObjectDefinition | undefined {\n return loadCustomObjects(dataDir).find((o) => o.name === name);\n}\n\n/** Add or update (by name) a custom object definition. */\nexport function defineCustomObject(dataDir: string, def: ObjectDefinition): ObjectDefinition[] {\n assertSafePathSegment(def.name, \"custom object name\");\n const objs = loadCustomObjects(dataDir);\n const idx = objs.findIndex((o) => o.name === def.name);\n if (idx >= 0) objs[idx] = def;\n else objs.push(def);\n writeJsonArray(objectsSchemaPath(dataDir), \"objects\", objs);\n return objs;\n}\n\nexport function listRecords(dataDir: string, name: string): ObjectRecord[] {\n return readJsonArray<ObjectRecord>(recordsPath(dataDir, name), \"records\");\n}\n\nfunction writeRecords(dataDir: string, name: string, records: ObjectRecord[]): void {\n writeJsonArray(recordsPath(dataDir, name), \"records\", records);\n}\n\nexport function getRecord(dataDir: string, name: string, id: string): ObjectRecord | undefined {\n return listRecords(dataDir, name).find((r) => r.id === id);\n}\n\nexport function createRecord(\n dataDir: string,\n name: string,\n values: Record<string, unknown>\n): RecordResult {\n const def = getObjectDefinition(dataDir, name);\n if (!def) return { ok: false, errors: [`Unknown object: ${name}`] };\n\n const validation = validateCustomFields(values, def.fields);\n if (!validation.valid) return { ok: false, errors: validation.errors };\n\n const now = new Date().toISOString();\n const record: ObjectRecord = {\n id: `${name}_${randomBytes(6).toString(\"hex\")}`,\n createdAt: now,\n updatedAt: now,\n values: validation.values,\n };\n writeRecords(dataDir, name, [...listRecords(dataDir, name), record]);\n return { ok: true, record };\n}\n\nexport function updateRecord(\n dataDir: string,\n name: string,\n id: string,\n values: Record<string, unknown>\n): RecordResult {\n const def = getObjectDefinition(dataDir, name);\n if (!def) return { ok: false, errors: [`Unknown object: ${name}`] };\n\n const records = listRecords(dataDir, name);\n const idx = records.findIndex((r) => r.id === id);\n if (idx < 0) return { ok: false, errors: [`Record not found: ${id}`] };\n\n const validation = validateCustomFields(values, def.fields);\n if (!validation.valid) return { ok: false, errors: validation.errors };\n\n const updated: ObjectRecord = {\n ...records[idx]!,\n updatedAt: new Date().toISOString(),\n values: { ...records[idx]!.values, ...validation.values },\n };\n records[idx] = updated;\n writeRecords(dataDir, name, records);\n return { ok: true, record: updated };\n}\n\nexport function deleteRecord(dataDir: string, name: string, id: string): boolean {\n const records = listRecords(dataDir, name);\n const next = records.filter((r) => r.id !== id);\n if (next.length === records.length) return false;\n writeRecords(dataDir, name, next);\n return true;\n}\n"],"mappings":";;;;;;AA+BA,SAAS,kBAAkB,SAAyB;CAClD,OAAO,KAAK,KAAK,SAAS,YAAY,UAAU,qBAAqB;AACvE;AACA,SAAS,YAAY,SAAiB,MAAsB;CAC1D,sBAAsB,MAAM,oBAAoB;CAChD,OAAO,KAAK,KAAK,SAAS,YAAY,WAAW,GAAG,KAAK,MAAM;AACjE;AAEA,SAAgB,kBAAkB,SAAqC;CACrE,OAAO,cAAgC,kBAAkB,OAAO,GAAG,SAAS;AAC9E;AAEA,SAAgB,oBAAoB,SAAiB,MAA4C;CAC/F,OAAO,kBAAkB,OAAO,EAAE,MAAM,MAAM,EAAE,SAAS,IAAI;AAC/D;;AAGA,SAAgB,mBAAmB,SAAiB,KAA2C;CAC7F,sBAAsB,IAAI,MAAM,oBAAoB;CACpD,MAAM,OAAO,kBAAkB,OAAO;CACtC,MAAM,MAAM,KAAK,WAAW,MAAM,EAAE,SAAS,IAAI,IAAI;CACrD,IAAI,OAAO,GAAG,KAAK,OAAO;MACrB,KAAK,KAAK,GAAG;CAClB,eAAe,kBAAkB,OAAO,GAAG,WAAW,IAAI;CAC1D,OAAO;AACT;AAEA,SAAgB,YAAY,SAAiB,MAA8B;CACzE,OAAO,cAA4B,YAAY,SAAS,IAAI,GAAG,SAAS;AAC1E;AAEA,SAAS,aAAa,SAAiB,MAAc,SAA+B;CAClF,eAAe,YAAY,SAAS,IAAI,GAAG,WAAW,OAAO;AAC/D;AAMA,SAAgB,aACd,SACA,MACA,QACc;CACd,MAAM,MAAM,oBAAoB,SAAS,IAAI;CAC7C,IAAI,CAAC,KAAK,OAAO;EAAE,IAAI;EAAO,QAAQ,CAAC,mBAAmB,MAAM;CAAE;CAElE,MAAM,aAAa,qBAAqB,QAAQ,IAAI,MAAM;CAC1D,IAAI,CAAC,WAAW,OAAO,OAAO;EAAE,IAAI;EAAO,QAAQ,WAAW;CAAO;CAErE,MAAM,uBAAM,IAAI,KAAK,GAAE,YAAY;CACnC,MAAM,SAAuB;EAC3B,IAAI,GAAG,KAAK,GAAG,YAAY,CAAC,EAAE,SAAS,KAAK;EAC5C,WAAW;EACX,WAAW;EACX,QAAQ,WAAW;CACrB;CACA,aAAa,SAAS,MAAM,CAAC,GAAG,YAAY,SAAS,IAAI,GAAG,MAAM,CAAC;CACnE,OAAO;EAAE,IAAI;EAAM;CAAO;AAC5B"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
|
|
2
|
+
import { t as assertSafePathSegment } from "./safe-path-mpp0dKtO.js";
|
|
1
3
|
import path from "path";
|
|
2
4
|
import fs from "fs";
|
|
3
5
|
import matter from "gray-matter";
|
|
@@ -26,7 +28,11 @@ const MainFactsSchema = z.object({
|
|
|
26
28
|
});
|
|
27
29
|
//#endregion
|
|
28
30
|
//#region src/fs/customer-dir.ts
|
|
31
|
+
function assertSafeSlug(slug) {
|
|
32
|
+
assertSafePathSegment(slug, "customer slug");
|
|
33
|
+
}
|
|
29
34
|
function getCustomerDir(dataDir, slug) {
|
|
35
|
+
assertSafeSlug(slug);
|
|
30
36
|
return path.join(dataDir, "customers", slug);
|
|
31
37
|
}
|
|
32
38
|
function customerExists(dataDir, slug) {
|
|
@@ -53,8 +59,7 @@ async function ensureCustomerDir(dataDir, slug) {
|
|
|
53
59
|
async function writeMainFacts(dataDir, slug, facts) {
|
|
54
60
|
const filePath = path.join(getCustomerDir(dataDir, slug), "main_facts.md");
|
|
55
61
|
const clean = Object.fromEntries(Object.entries(facts).filter(([, v]) => v !== void 0));
|
|
56
|
-
|
|
57
|
-
fs.writeFileSync(filePath, content, "utf-8");
|
|
62
|
+
writeFileAtomic(filePath, matter.stringify("", clean));
|
|
58
63
|
}
|
|
59
64
|
async function readMainFacts(dataDir, slug) {
|
|
60
65
|
const filePath = path.join(getCustomerDir(dataDir, slug), "main_facts.md");
|
|
@@ -70,6 +75,6 @@ async function readMainFacts(dataDir, slug) {
|
|
|
70
75
|
return result.data;
|
|
71
76
|
}
|
|
72
77
|
//#endregion
|
|
73
|
-
export {
|
|
78
|
+
export { readMainFacts as a, listCustomerSlugs as i, customerExists as n, writeMainFacts as o, ensureCustomerDir as r, MainFactsSchema as s, assertSafeSlug as t };
|
|
74
79
|
|
|
75
|
-
//# sourceMappingURL=customer-dir-
|
|
80
|
+
//# sourceMappingURL=customer-dir-CkMMXhb0.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customer-dir-CkMMXhb0.js","names":[],"sources":["../src/schemas/main-facts.ts","../src/fs/customer-dir.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const MainFactsSchema = z.object({\n name: z.string().min(1),\n domain: z.string().optional(),\n email: z.string().optional(),\n phone: z.string().optional(),\n industry: z.string().optional(),\n relationship_stage: z.enum([\"prospect\", \"active\", \"churned\", \"paused\"]),\n deal_value: z.number().optional(),\n currency: z.string().default(\"EUR\"),\n primary_contact: z.string().optional(),\n timezone: z.string().optional(),\n tags: z.array(z.string()).default([]),\n created: z.preprocess(\n (v) => (v instanceof Date ? v.toISOString().slice(0, 10) : v),\n z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\")\n ),\n updated: z.preprocess(\n (v) => (v instanceof Date ? v.toISOString().slice(0, 10) : v),\n z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\")\n ),\n});\n\nexport type MainFacts = z.infer<typeof MainFactsSchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { fromZodError } from \"zod-validation-error\";\nimport { MainFactsSchema, type MainFacts } from \"../schemas/main-facts.js\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\nimport { isSafePathSegment, assertSafePathSegment } from \"./safe-path.js\";\n\n/** A customer slug is safe iff it is a safe filesystem path segment. */\nexport function isSafeSlug(slug: unknown): slug is string {\n return isSafePathSegment(slug);\n}\n\nexport function assertSafeSlug(slug: string): void {\n assertSafePathSegment(slug, \"customer slug\");\n}\n\nexport function getCustomerDir(dataDir: string, slug: string): string {\n assertSafeSlug(slug);\n return path.join(dataDir, \"customers\", slug);\n}\n\nexport function customerExists(dataDir: string, slug: string): boolean {\n return fs.existsSync(getCustomerDir(dataDir, slug));\n}\n\n/** List all customer slugs (immediate subdirectories of customers/). */\nexport function listCustomerSlugs(dataDir: string): string[] {\n const dir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(dir)) return [];\n return fs.readdirSync(dir).filter((s) => {\n try {\n return fs.statSync(path.join(dir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n}\n\nexport async function ensureCustomerDir(dataDir: string, slug: string): Promise<void> {\n const customerDir = getCustomerDir(dataDir, slug);\n fs.mkdirSync(customerDir, { recursive: true });\n fs.mkdirSync(path.join(customerDir, \"attachments\"), { recursive: true });\n fs.mkdirSync(path.join(customerDir, \"transcripts\"), { recursive: true });\n}\n\nexport async function writeMainFacts(\n dataDir: string,\n slug: string,\n facts: MainFacts\n): Promise<void> {\n const filePath = path.join(getCustomerDir(dataDir, slug), \"main_facts.md\");\n // Strip undefined values — gray-matter YAML serializer rejects them\n const clean = Object.fromEntries(\n Object.entries(facts as Record<string, unknown>).filter(([, v]) => v !== undefined)\n );\n const content = matter.stringify(\"\", clean);\n writeFileAtomic(filePath, content);\n}\n\nexport async function readMainFacts(dataDir: string, slug: string): Promise<MainFacts> {\n const filePath = path.join(getCustomerDir(dataDir, slug), \"main_facts.md\");\n if (!fs.existsSync(filePath)) {\n throw new Error(`main_facts.md not found for customer '${slug}'`);\n }\n // Use fs.readFileSync so the memfs mock is respected in tests,\n // then parse the string with matter.\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n const raw = matter(content);\n // gray-matter parses YYYY-MM-DD as Date objects; coerce back to strings for Zod\n const data = raw.data as Record<string, unknown>;\n for (const key of [\"created\", \"updated\"] as const) {\n if (data[key] instanceof Date) {\n data[key] = (data[key] as Date).toISOString().slice(0, 10);\n }\n }\n const result = MainFactsSchema.safeParse(data);\n if (!result.success) {\n throw new Error(\n fromZodError(result.error, {\n prefix: `Schema error in ${filePath}`,\n prefixSeparator: \":\\n - \",\n issueSeparator: \"\\n - \",\n }).message\n );\n }\n return result.data;\n}\n"],"mappings":";;;;;;;;AAEA,MAAa,kBAAkB,EAAE,OAAO;CACtC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;CACtB,QAAQ,EAAE,OAAO,EAAE,SAAS;CAC5B,OAAO,EAAE,OAAO,EAAE,SAAS;CAC3B,OAAO,EAAE,OAAO,EAAE,SAAS;CAC3B,UAAU,EAAE,OAAO,EAAE,SAAS;CAC9B,oBAAoB,EAAE,KAAK;EAAC;EAAY;EAAU;EAAW;CAAQ,CAAC;CACtE,YAAY,EAAE,OAAO,EAAE,SAAS;CAChC,UAAU,EAAE,OAAO,EAAE,QAAQ,KAAK;CAClC,iBAAiB,EAAE,OAAO,EAAE,SAAS;CACrC,UAAU,EAAE,OAAO,EAAE,SAAS;CAC9B,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;CACpC,SAAS,EAAE,YACR,MAAO,aAAa,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,IAAI,GAC3D,EAAE,OAAO,EAAE,MAAM,uBAAuB,qBAAqB,CAC/D;CACA,SAAS,EAAE,YACR,MAAO,aAAa,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,IAAI,GAC3D,EAAE,OAAO,EAAE,MAAM,uBAAuB,qBAAqB,CAC/D;AACF,CAAC;;;ACTD,SAAgB,eAAe,MAAoB;CACjD,sBAAsB,MAAM,eAAe;AAC7C;AAEA,SAAgB,eAAe,SAAiB,MAAsB;CACpE,eAAe,IAAI;CACnB,OAAO,KAAK,KAAK,SAAS,aAAa,IAAI;AAC7C;AAEA,SAAgB,eAAe,SAAiB,MAAuB;CACrE,OAAO,GAAG,WAAW,eAAe,SAAS,IAAI,CAAC;AACpD;;AAGA,SAAgB,kBAAkB,SAA2B;CAC3D,MAAM,MAAM,KAAK,KAAK,SAAS,WAAW;CAC1C,IAAI,CAAC,GAAG,WAAW,GAAG,GAAG,OAAO,CAAC;CACjC,OAAO,GAAG,YAAY,GAAG,EAAE,QAAQ,MAAM;EACvC,IAAI;GACF,OAAO,GAAG,SAAS,KAAK,KAAK,KAAK,CAAC,CAAC,EAAE,YAAY;EACpD,QAAQ;GACN,OAAO;EACT;CACF,CAAC;AACH;AAEA,eAAsB,kBAAkB,SAAiB,MAA6B;CACpF,MAAM,cAAc,eAAe,SAAS,IAAI;CAChD,GAAG,UAAU,aAAa,EAAE,WAAW,KAAK,CAAC;CAC7C,GAAG,UAAU,KAAK,KAAK,aAAa,aAAa,GAAG,EAAE,WAAW,KAAK,CAAC;CACvE,GAAG,UAAU,KAAK,KAAK,aAAa,aAAa,GAAG,EAAE,WAAW,KAAK,CAAC;AACzE;AAEA,eAAsB,eACpB,SACA,MACA,OACe;CACf,MAAM,WAAW,KAAK,KAAK,eAAe,SAAS,IAAI,GAAG,eAAe;CAEzE,MAAM,QAAQ,OAAO,YACnB,OAAO,QAAQ,KAAgC,EAAE,QAAQ,GAAG,OAAO,MAAM,KAAA,CAAS,CACpF;CAEA,gBAAgB,UADA,OAAO,UAAU,IAAI,KACL,CAAC;AACnC;AAEA,eAAsB,cAAc,SAAiB,MAAkC;CACrF,MAAM,WAAW,KAAK,KAAK,eAAe,SAAS,IAAI,GAAG,eAAe;CACzE,IAAI,CAAC,GAAG,WAAW,QAAQ,GACzB,MAAM,IAAI,MAAM,yCAAyC,KAAK,EAAE;CAOlE,MAAM,OAFM,OADI,GAAG,aAAa,UAAU,OACjB,CAEV,EAAE;CACjB,KAAK,MAAM,OAAO,CAAC,WAAW,SAAS,GACrC,IAAI,KAAK,gBAAgB,MACvB,KAAK,OAAQ,KAAK,KAAc,YAAY,EAAE,MAAM,GAAG,EAAE;CAG7D,MAAM,SAAS,gBAAgB,UAAU,IAAI;CAC7C,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,MACR,aAAa,OAAO,OAAO;EACzB,QAAQ,mBAAmB;EAC3B,iBAAiB;EACjB,gBAAgB;CAClB,CAAC,EAAE,OACL;CAEF,OAAO,OAAO;AAChB"}
|