@hotmeshio/long-tail 0.8.3 → 0.8.5
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/build/api/escalations/create.js +19 -60
- package/build/api/escalations/index.d.ts +1 -1
- package/build/api/escalations/index.js +2 -1
- package/build/api/escalations/resolve.d.ts +28 -0
- package/build/api/escalations/resolve.js +110 -4
- package/build/api/escalations/single.js +8 -4
- package/build/api/roles.d.ts +1 -0
- package/build/lib/db/index.js +12 -1
- package/build/lib/db/migrate.js +27 -0
- package/build/modules/defaults.d.ts +2 -0
- package/build/modules/defaults.js +3 -1
- package/build/routes/escalations/resolve.js +18 -0
- package/build/scripts/twin-farm.d.ts +12 -13
- package/build/scripts/twin-farm.js +61 -75
- package/build/sdk/index.d.ts +8 -0
- package/build/sdk/index.js +1 -0
- package/build/services/escalation/crud.d.ts +36 -0
- package/build/services/escalation/crud.js +53 -0
- package/build/services/escalation/queries.d.ts +13 -0
- package/build/services/escalation/queries.js +27 -0
- package/build/services/maintenance/index.js +22 -0
- package/build/services/orchestrator/condition.d.ts +18 -12
- package/build/services/orchestrator/condition.js +11 -8
- package/build/services/role/index.d.ts +1 -2
- package/build/services/role/index.js +1 -2
- package/build/services/role/schema-validator.d.ts +9 -0
- package/build/services/role/schema-validator.js +42 -0
- package/build/services/role/sql.d.ts +1 -1
- package/build/services/role/sql.js +2 -2
- package/build/services/role/types.d.ts +8 -6
- package/build/system/mcp-servers/admin/schemas.js +4 -2
- package/build/system/mcp-servers/human-queue-schemas.js +6 -2
- package/build/system/mcp-servers/human-queue.js +11 -3
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/config.d.ts +2 -0
- package/build/types/maintenance.d.ts +6 -1
- package/build/types/startup.d.ts +5 -1
- package/dashboard/dist/assets/{AdminDashboard-Dhr7sUq6.js → AdminDashboard-RrSvb8d-.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-Dhr7sUq6.js.map → AdminDashboard-RrSvb8d-.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-CUPm8FjH.js → AgentConfigPage-5RMdI0ku.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-CUPm8FjH.js.map → AgentConfigPage-5RMdI0ku.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-C9Tmmhhc.js → AgentDetailPage-C_-sIBcr.js} +2 -2
- package/dashboard/dist/assets/{AgentDetailPage-C9Tmmhhc.js.map → AgentDetailPage-C_-sIBcr.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-Pj47ZJ2U.js → AgentsPage-CxNNtSsf.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-Pj47ZJ2U.js.map → AgentsPage-CxNNtSsf.js.map} +1 -1
- package/dashboard/dist/assets/{AvailableEscalationsPage-BxakNqha.js → AvailableEscalationsPage-BcbhA6NE.js} +2 -2
- package/dashboard/dist/assets/{AvailableEscalationsPage-BxakNqha.js.map → AvailableEscalationsPage-BcbhA6NE.js.map} +1 -1
- package/dashboard/dist/assets/{BotPicker-CCIo5ic0.js → BotPicker-BQVz6bye.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-CCIo5ic0.js.map → BotPicker-BQVz6bye.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-XFVq4iPX.js → CapabilitiesPage-DlZ8SgO3.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-XFVq4iPX.js.map → CapabilitiesPage-DlZ8SgO3.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-CBKICsCu.js → CollapsibleSection-lo8Qx4zb.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-CBKICsCu.js.map → CollapsibleSection-lo8Qx4zb.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-BN_d7ybc.js → CredentialsPage-DJ9DyXsO.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-BN_d7ybc.js.map → CredentialsPage-DJ9DyXsO.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-4V_EXMUg.js → CronLabel-o0dbisw1.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-4V_EXMUg.js.map → CronLabel-o0dbisw1.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-BdurQj-B.js → CustomDurationPicker-CGiwKSVJ.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-BdurQj-B.js.map → CustomDurationPicker-CGiwKSVJ.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-CO8XMSJX.js → ElapsedCell-N22STQcg.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-CO8XMSJX.js.map → ElapsedCell-N22STQcg.js.map} +1 -1
- package/dashboard/dist/assets/EscalationSchemaPage-Zsmewko2.js +9 -0
- package/dashboard/dist/assets/EscalationSchemaPage-Zsmewko2.js.map +1 -0
- package/dashboard/dist/assets/{EscalationsOverview-qlo0mxxa.js → EscalationsOverview-CE2p70Ct.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-qlo0mxxa.js.map → EscalationsOverview-CE2p70Ct.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-D9RjAT_a.js → EventTable-B3Gj7Ayg.js} +2 -2
- package/dashboard/dist/assets/{EventTable-D9RjAT_a.js.map → EventTable-B3Gj7Ayg.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-CU3r2tzN.js → GraphInvokePage-DywWVSdj.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-CU3r2tzN.js.map → GraphInvokePage-DywWVSdj.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-B7pb4-Gx.js → HomePage-CxtBiULd.js} +2 -2
- package/dashboard/dist/assets/{HomePage-B7pb4-Gx.js.map → HomePage-CxtBiULd.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-C_64Y22Z.js → ListToolbar-Ok2O0wmC.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-C_64Y22Z.js.map → ListToolbar-Ok2O0wmC.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-D-4eSOTc.js → McpOverview-CTCYZXIp.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-D-4eSOTc.js.map → McpOverview-CTCYZXIp.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-BYwDb8LC.js → McpQueryDetailPage-BOaXf0bZ.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-BYwDb8LC.js.map → McpQueryDetailPage-BOaXf0bZ.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-CIijB5t2.js → McpQueryPage-BbYojoHu.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-CIijB5t2.js.map → McpQueryPage-BbYojoHu.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-CQmhfgju.js → McpRunDetailPage-CXGCVmPq.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-CQmhfgju.js.map → McpRunDetailPage-CXGCVmPq.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-CdDyeOdi.js → McpRunsPage-Df1GqxUg.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-CdDyeOdi.js.map → McpRunsPage-Df1GqxUg.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-DgZYi-BC.js +2 -0
- package/dashboard/dist/assets/OperationsPage-DgZYi-BC.js.map +1 -0
- package/dashboard/dist/assets/{OperatorDashboard-DFORmS46.js → OperatorDashboard-DIR7XDeD.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-DFORmS46.js.map → OperatorDashboard-DIR7XDeD.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-DwypVFE2.js → ProcessDetailPage-BuSi654J.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-DwypVFE2.js.map → ProcessDetailPage-BuSi654J.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-tNLvALwT.js → ProcessesListPage-Dhik7XpW.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-tNLvALwT.js.map → ProcessesListPage-Dhik7XpW.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-BfzqQxMU.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-BfzqQxMU.js.map +1 -0
- package/dashboard/dist/assets/{RolesPage-txueySPI.js → RolesPage-BRicD5mx.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-txueySPI.js.map → RolesPage-BRicD5mx.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-Bvefon9B.js → RunAsSelector-DipyBf_T.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-Bvefon9B.js.map → RunAsSelector-DipyBf_T.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-CeQCzVIA.js → StickyPagination-CD4W450h.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-CeQCzVIA.js.map → StickyPagination-CD4W450h.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-6wEm6o6t.js → StreamMessageDetail-BbDdWDUY.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-6wEm6o6t.js.map → StreamMessageDetail-BbDdWDUY.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-C97Rldf5.js → SwimlaneTimeline-Bktv2vi5.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-C97Rldf5.js.map → SwimlaneTimeline-Bktv2vi5.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-B09mLOCE.js → TaskDetailPage-pSU3xuG9.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-B09mLOCE.js.map → TaskDetailPage-pSU3xuG9.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-CQhiSGkN.js → TasksListPage-M6ok-Nfd.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-CQhiSGkN.js.map → TasksListPage-M6ok-Nfd.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-BkyeBcMF.js → TimeAgo-DVWhRNbi.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-BkyeBcMF.js.map → TimeAgo-DVWhRNbi.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-DVp_UdEB.js → TimestampCell-rG7pmIkW.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-DVp_UdEB.js.map → TimestampCell-rG7pmIkW.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-CZOuRP_i.js → ToolTestPanel-CLcMA8OE.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-CZOuRP_i.js.map → ToolTestPanel-CLcMA8OE.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-0mvQde8R.js → TopicDetailPage-C9gvoWYu.js} +2 -2
- package/dashboard/dist/assets/{TopicDetailPage-0mvQde8R.js.map → TopicDetailPage-C9gvoWYu.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-DkvsHLLe.js → TopicsPage-CIGi3TdT.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-DkvsHLLe.js.map → TopicsPage-CIGi3TdT.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-EaD_Tz9y.js → UserName-CifbOahk.js} +2 -2
- package/dashboard/dist/assets/{UserName-EaD_Tz9y.js.map → UserName-CifbOahk.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-CjMSQ9Bb.js → WorkflowExecutionPage-BR0oVx_p.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-CjMSQ9Bb.js.map → WorkflowExecutionPage-BR0oVx_p.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-BgUz_jO6.js → WorkflowsDashboard-CSYrYQsO.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-BgUz_jO6.js.map → WorkflowsDashboard-CSYrYQsO.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-BzUjUfAz.js → WorkflowsOverview-CaXWYZuw.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-BzUjUfAz.js.map → WorkflowsOverview-CaXWYZuw.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-C48Ui513.js → YamlWorkflowDetailPage-CRBXahFJ.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-C48Ui513.js.map → YamlWorkflowDetailPage-CRBXahFJ.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-CQ-dk84N.js → YamlWorkflowsPage-D5zc8Lta.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-CQ-dk84N.js.map → YamlWorkflowsPage-D5zc8Lta.js.map} +1 -1
- package/dashboard/dist/assets/{agents-O1GlQE9p.js → agents-D7vYXeLj.js} +2 -2
- package/dashboard/dist/assets/{agents-O1GlQE9p.js.map → agents-D7vYXeLj.js.map} +1 -1
- package/dashboard/dist/assets/{bots-DB5Z7MEk.js → bots-CKaYN2fL.js} +2 -2
- package/dashboard/dist/assets/{bots-DB5Z7MEk.js.map → bots-CKaYN2fL.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-Du9EfNdT.js → capabilities-BknM6oFm.js} +2 -2
- package/dashboard/dist/assets/{capabilities-Du9EfNdT.js.map → capabilities-BknM6oFm.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-ku4V2nVk.js → controlplane-BodNGCr-.js} +2 -2
- package/dashboard/dist/assets/{controlplane-ku4V2nVk.js.map → controlplane-BodNGCr-.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-oVVz7Lw2.js → escalation-CH_XK2XM.js} +2 -2
- package/dashboard/dist/assets/{escalation-oVVz7Lw2.js.map → escalation-CH_XK2XM.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-columns-C-xaR_Qg.js → escalation-columns-CcbLKRI4.js} +2 -2
- package/dashboard/dist/assets/{escalation-columns-C-xaR_Qg.js.map → escalation-columns-CcbLKRI4.js.map} +1 -1
- package/dashboard/dist/assets/index--xNp5rx6.css +1 -0
- package/dashboard/dist/assets/{index-aHhMmZ0W.js → index-B-LQ8fsJ.js} +2 -2
- package/dashboard/dist/assets/{index-aHhMmZ0W.js.map → index-B-LQ8fsJ.js.map} +1 -1
- package/dashboard/dist/assets/{index-CyXilW90.js → index-BH67e1Ah.js} +2 -2
- package/dashboard/dist/assets/{index-CyXilW90.js.map → index-BH67e1Ah.js.map} +1 -1
- package/dashboard/dist/assets/{index-MbEPoyTe.js → index-B_aFcGv9.js} +4 -4
- package/dashboard/dist/assets/{index-MbEPoyTe.js.map → index-B_aFcGv9.js.map} +1 -1
- package/dashboard/dist/assets/{index-DsKK1fCc.js → index-Bwofyfi8.js} +2 -2
- package/dashboard/dist/assets/{index-DsKK1fCc.js.map → index-Bwofyfi8.js.map} +1 -1
- package/dashboard/dist/assets/{index-61yKjTkk.js → index-C3QKGY5C.js} +2 -2
- package/dashboard/dist/assets/{index-61yKjTkk.js.map → index-C3QKGY5C.js.map} +1 -1
- package/dashboard/dist/assets/{index-BwXtXaw8.js → index-CJpywoA8.js} +2 -2
- package/dashboard/dist/assets/{index-BwXtXaw8.js.map → index-CJpywoA8.js.map} +1 -1
- package/dashboard/dist/assets/index-CV8soXIM.js +5 -0
- package/dashboard/dist/assets/index-CV8soXIM.js.map +1 -0
- package/dashboard/dist/assets/{index-CxlsLEr1.js → index-CdN-Z8-r.js} +2 -2
- package/dashboard/dist/assets/{index-CxlsLEr1.js.map → index-CdN-Z8-r.js.map} +1 -1
- package/dashboard/dist/assets/{index-DcZDyLHC.js → index-D7NHjvA4.js} +2 -2
- package/dashboard/dist/assets/{index-DcZDyLHC.js.map → index-D7NHjvA4.js.map} +1 -1
- package/dashboard/dist/assets/{index-DkZnXWTA.js → index-DHhz71yv.js} +2 -2
- package/dashboard/dist/assets/{index-DkZnXWTA.js.map → index-DHhz71yv.js.map} +1 -1
- package/dashboard/dist/assets/{index-DgAmkmtv.js → index-Gt2Rebs9.js} +2 -2
- package/dashboard/dist/assets/{index-DgAmkmtv.js.map → index-Gt2Rebs9.js.map} +1 -1
- package/dashboard/dist/assets/index-IIq5Tkqs.js +6 -0
- package/dashboard/dist/assets/index-IIq5Tkqs.js.map +1 -0
- package/dashboard/dist/assets/{index-Bcgsf2yD.js → index-_gh2K9DU.js} +2 -2
- package/dashboard/dist/assets/{index-Bcgsf2yD.js.map → index-_gh2K9DU.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-CTjJVvIG.js → knowledge-CE6gjN3G.js} +2 -2
- package/dashboard/dist/assets/{knowledge-CTjJVvIG.js.map → knowledge-CE6gjN3G.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-CsHkygRS.js → mcp-B4F-T-7R.js} +2 -2
- package/dashboard/dist/assets/{mcp-CsHkygRS.js.map → mcp-B4F-T-7R.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-BCt4tpjO.js → mcp-query-CYOarL1K.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-BCt4tpjO.js.map → mcp-query-CYOarL1K.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-BpGmuS5V.js → pipelines-B4gB9qMx.js} +2 -2
- package/dashboard/dist/assets/{pipelines-BpGmuS5V.js.map → pipelines-B4gB9qMx.js.map} +1 -1
- package/dashboard/dist/assets/{roles-lHLgLwOY.js → roles-LZf98ePg.js} +2 -2
- package/dashboard/dist/assets/{roles-lHLgLwOY.js.map → roles-LZf98ePg.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-BHfimh-E.js → tasks-GG8c_U9w.js} +2 -2
- package/dashboard/dist/assets/{tasks-BHfimh-E.js.map → tasks-GG8c_U9w.js.map} +1 -1
- package/dashboard/dist/assets/{topics-BW2cS79q.js → topics-C7-6oOgA.js} +2 -2
- package/dashboard/dist/assets/{topics-BW2cS79q.js.map → topics-C7-6oOgA.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-CeOIM1Ix.js → useEventHooks-82IiMmOc.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-CeOIM1Ix.js.map → useEventHooks-82IiMmOc.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-CEVLPGGY.js → useNamespace-DQEqD2LA.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-CEVLPGGY.js.map → useNamespace-DQEqD2LA.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-DdRNcy7s.js → useYamlActivityEvents-Bm0-ewLx.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-DdRNcy7s.js.map → useYamlActivityEvents-Bm0-ewLx.js.map} +1 -1
- package/dashboard/dist/assets/{users-CAih5Fty.js → users-DHx2ium7.js} +2 -2
- package/dashboard/dist/assets/{users-CAih5Fty.js.map → users-DHx2ium7.js.map} +1 -1
- package/dashboard/dist/assets/{workflows-C_gCwB9q.js → workflows-qIbkeQ_5.js} +2 -2
- package/dashboard/dist/assets/{workflows-C_gCwB9q.js.map → workflows-qIbkeQ_5.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-CPabf8Sf.js → yaml-workflows-FibyeHpo.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-CPabf8Sf.js.map → yaml-workflows-FibyeHpo.js.map} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/docs/api/http/escalations.md +28 -4
- package/docs/api/http/roles.md +1 -1
- package/docs/api/http/workflows.md +1 -1
- package/docs/api/sdk/escalations.md +40 -5
- package/docs/api/sdk/workflows.md +1 -1
- package/docs/dashboard.md +3 -3
- package/docs/data.md +2 -2
- package/docs/hitl-guide.md +114 -6
- package/docs/maintenance.md +6 -3
- package/docs/oauth-and-delegation.md +1 -1
- package/package.json +2 -2
- package/dashboard/dist/assets/EscalationSchemaPage-hzFtuyha.js +0 -9
- package/dashboard/dist/assets/EscalationSchemaPage-hzFtuyha.js.map +0 -1
- package/dashboard/dist/assets/OperationsPage-Cz_0K8tj.js +0 -2
- package/dashboard/dist/assets/OperationsPage-Cz_0K8tj.js.map +0 -1
- package/dashboard/dist/assets/RoleDetailPage-BPsrUqIa.js +0 -11
- package/dashboard/dist/assets/RoleDetailPage-BPsrUqIa.js.map +0 -1
- package/dashboard/dist/assets/index-BdqKh0UK.css +0 -1
- package/dashboard/dist/assets/index-CAzFU1rn.js +0 -9
- package/dashboard/dist/assets/index-CAzFU1rn.js.map +0 -1
- package/dashboard/dist/assets/index-D0bDVU2a.js +0 -6
- package/dashboard/dist/assets/index-D0bDVU2a.js.map +0 -1
|
@@ -32,34 +32,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
36
|
exports.createEscalation = createEscalation;
|
|
40
|
-
const ajv_1 = __importDefault(require("ajv"));
|
|
41
37
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
42
38
|
const roleService = __importStar(require("../../services/role"));
|
|
39
|
+
const schema_validator_1 = require("../../services/role/schema-validator");
|
|
43
40
|
const escalation_1 = require("../../types/escalation");
|
|
44
41
|
const helpers_1 = require("./helpers");
|
|
45
|
-
// strict: false — role metadata_schemas use the project's house extension
|
|
46
|
-
// keywords (x-lt-widget etc., the same style as seeded form_schemas); Ajv 8's
|
|
47
|
-
// strict mode throws at compile on any unknown keyword, which would turn every
|
|
48
|
-
// metadata-bearing create for that role into a 500.
|
|
49
|
-
const ajv = new ajv_1.default({ allErrors: true, strict: false });
|
|
50
|
-
// Compiled validators cached per role+version, keyed by the schema's serialized
|
|
51
|
-
// form so an admin's schema edit invalidates the stale validator on the next
|
|
52
|
-
// create. Pinned versions are immutable, but the same key logic covers both.
|
|
53
|
-
const validatorCache = new Map();
|
|
54
|
-
function compileRoleValidator(cacheId, schema) {
|
|
55
|
-
const key = JSON.stringify(schema);
|
|
56
|
-
const cached = validatorCache.get(cacheId);
|
|
57
|
-
if (cached && cached.key === key)
|
|
58
|
-
return cached.validate;
|
|
59
|
-
const validate = ajv.compile(schema);
|
|
60
|
-
validatorCache.set(cacheId, { key, validate });
|
|
61
|
-
return validate;
|
|
62
|
-
}
|
|
63
42
|
// ── Create ────────────────────────────────────────────────────────────────
|
|
64
43
|
/**
|
|
65
44
|
* Create a standalone escalation (not tied to a workflow).
|
|
@@ -94,46 +73,26 @@ async function createEscalation(input, auth) {
|
|
|
94
73
|
if (denied) {
|
|
95
74
|
return { status: 403, error: `You must have write access to the "${role}" role or be a superadmin to create escalations for it` };
|
|
96
75
|
}
|
|
97
|
-
// Validate metadata against the role's
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return { status: 400, error: `Schema version ${pinned} does not exist for role "${role}"` };
|
|
110
|
-
}
|
|
111
|
-
schema = snapshot.metadata_schema;
|
|
76
|
+
// Validate the caller-supplied metadata against the role's metadata_schema
|
|
77
|
+
// (the queryable-facet contract). An escalation may carry a form-version pin
|
|
78
|
+
// (metadata.schema_version) chosen by the workflow author; that is a system
|
|
79
|
+
// key, not caller data, so it is excluded from validation. No version is
|
|
80
|
+
// stamped here — omitting it means the resolve UI renders the role's latest
|
|
81
|
+
// form at fetch time.
|
|
82
|
+
const metadata = input.metadata ?? {};
|
|
83
|
+
const metadataSchema = await roleService.getRoleMetadataSchema(role);
|
|
84
|
+
if (metadataSchema) {
|
|
85
|
+
let validate;
|
|
86
|
+
try {
|
|
87
|
+
validate = (0, schema_validator_1.compileSchemaValidator)(`${role}:meta`, metadataSchema);
|
|
112
88
|
}
|
|
113
|
-
|
|
114
|
-
schema
|
|
89
|
+
catch (compileErr) {
|
|
90
|
+
// A broken stored schema is the role admin's bug, not the creator's.
|
|
91
|
+
return { status: 422, error: `metadata_schema for role "${role}" is not a valid JSON Schema: ${compileErr.message}` };
|
|
115
92
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
validate = compileRoleValidator(`${role}@${pinned ?? 'live'}`, schema);
|
|
120
|
-
}
|
|
121
|
-
catch (compileErr) {
|
|
122
|
-
// A broken stored schema is the role admin's bug, not the creator's —
|
|
123
|
-
// name it explicitly instead of surfacing an opaque 500.
|
|
124
|
-
return {
|
|
125
|
-
status: 422,
|
|
126
|
-
error: `metadata_schema for role "${role}" is not a valid JSON Schema: ${compileErr.message}`,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
// The pin is a system key, not caller data — strip it so schemas with
|
|
130
|
-
// additionalProperties: false still validate the caller's bag.
|
|
131
|
-
const { [escalation_1.ESCALATION_METADATA_KEYS.SCHEMA_VERSION]: _pin, ...callerMetadata } = input.metadata;
|
|
132
|
-
const valid = validate(callerMetadata);
|
|
133
|
-
if (!valid) {
|
|
134
|
-
const msgs = (validate.errors ?? []).map((e) => `${e.instancePath || '(root)'} ${e.message}`).join('; ');
|
|
135
|
-
return { status: 400, error: `metadata does not match the role schema: ${msgs}` };
|
|
136
|
-
}
|
|
93
|
+
const { [escalation_1.ESCALATION_METADATA_KEYS.SCHEMA_VERSION]: _pin, ...callerMetadata } = metadata;
|
|
94
|
+
if (!validate(callerMetadata)) {
|
|
95
|
+
return { status: 400, error: `metadata does not match the role schema: ${(0, schema_validator_1.formatValidationErrors)(validate)}` };
|
|
137
96
|
}
|
|
138
97
|
}
|
|
139
98
|
const escalation = await escalationService.createEscalation({
|
|
@@ -3,7 +3,7 @@ export { listEscalations, listAvailableEscalations, listDistinctTypes, listFacet
|
|
|
3
3
|
export { getEscalation, getEscalationsByWorkflowId, escalateToRole } from './single';
|
|
4
4
|
export { claimEscalation, releaseEscalation } from './claim';
|
|
5
5
|
export { releaseExpiredClaims, updatePriority, bulkClaim, bulkAssign, bulkEscalate, bulkTriage } from './bulk';
|
|
6
|
-
export { resolveEscalation, resolveBySignalKey, resolveByIds } from './resolve';
|
|
6
|
+
export { resolveEscalation, resolveBySignalKey, resolveByIds, resolveAllOrNone } from './resolve';
|
|
7
7
|
export { findByMetadata, claimByMetadata, resolveByMetadata } from './metadata';
|
|
8
8
|
export { cancelSingleEscalation, bulkCancel } from './cancel';
|
|
9
9
|
export { searchByFacets, claimGroups, claimByFacets } from './facets';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.claimByFacets = exports.claimGroups = exports.searchByFacets = exports.bulkCancel = exports.cancelSingleEscalation = exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveByIds = exports.resolveBySignalKey = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getStationMetrics = exports.getEscalationStats = exports.listFacetKeys = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
|
|
3
|
+
exports.claimByFacets = exports.claimGroups = exports.searchByFacets = exports.bulkCancel = exports.cancelSingleEscalation = exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveAllOrNone = exports.resolveByIds = exports.resolveBySignalKey = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getStationMetrics = exports.getEscalationStats = exports.listFacetKeys = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
|
|
4
4
|
var create_1 = require("./create");
|
|
5
5
|
Object.defineProperty(exports, "createEscalation", { enumerable: true, get: function () { return create_1.createEscalation; } });
|
|
6
6
|
var list_1 = require("./list");
|
|
@@ -28,6 +28,7 @@ var resolve_1 = require("./resolve");
|
|
|
28
28
|
Object.defineProperty(exports, "resolveEscalation", { enumerable: true, get: function () { return resolve_1.resolveEscalation; } });
|
|
29
29
|
Object.defineProperty(exports, "resolveBySignalKey", { enumerable: true, get: function () { return resolve_1.resolveBySignalKey; } });
|
|
30
30
|
Object.defineProperty(exports, "resolveByIds", { enumerable: true, get: function () { return resolve_1.resolveByIds; } });
|
|
31
|
+
Object.defineProperty(exports, "resolveAllOrNone", { enumerable: true, get: function () { return resolve_1.resolveAllOrNone; } });
|
|
31
32
|
var metadata_1 = require("./metadata");
|
|
32
33
|
Object.defineProperty(exports, "findByMetadata", { enumerable: true, get: function () { return metadata_1.findByMetadata; } });
|
|
33
34
|
Object.defineProperty(exports, "claimByMetadata", { enumerable: true, get: function () { return metadata_1.claimByMetadata; } });
|
|
@@ -44,3 +44,31 @@ export declare function resolveByIds(input: {
|
|
|
44
44
|
resolverPayload: Record<string, any>;
|
|
45
45
|
metadata?: Record<string, any>;
|
|
46
46
|
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
47
|
+
/**
|
|
48
|
+
* Atomic all-or-none bulk resolve with PER-ROW payloads — the gang-handoff
|
|
49
|
+
* sibling of {@link resolveByIds}. Every listed escalation must be pending and
|
|
50
|
+
* writable by the caller, or NOTHING resolves. Unlike resolveByIds, rows
|
|
51
|
+
* backing a live `condition()` waiter are first-class: each is woken with its
|
|
52
|
+
* own payload inside the single atomic statement (same wake contract as
|
|
53
|
+
* single resolve).
|
|
54
|
+
*
|
|
55
|
+
* RBAC matches resolveByIds: per-item write scope, 404 non-disclosure when any
|
|
56
|
+
* id is missing or out of scope, nothing resolved. `requireClaimed` adds a
|
|
57
|
+
* SQL-level assertion that every row is currently assigned to the caller —
|
|
58
|
+
* for claim-then-resolve flows (a broker resolving its claimed gang), this
|
|
59
|
+
* closes the window where another principal re-claims a member mid-flight.
|
|
60
|
+
*
|
|
61
|
+
* Rows whose resolution requires a non-atomic path (legacy signal routing via
|
|
62
|
+
* `metadata.signal_id` / `metadata.signal_routing`, which need a separate
|
|
63
|
+
* workflow signal) block the batch with 409 — bulk-resolving them would strand
|
|
64
|
+
* their workflows. Re-run/triage rows (workflow_type without any signal shape)
|
|
65
|
+
* resolve as bookkeeping acknowledgements, matching resolveByIds semantics.
|
|
66
|
+
*/
|
|
67
|
+
export declare function resolveAllOrNone(input: {
|
|
68
|
+
items: Array<{
|
|
69
|
+
id: string;
|
|
70
|
+
resolverPayload: Record<string, any>;
|
|
71
|
+
}>;
|
|
72
|
+
metadata?: Record<string, any>;
|
|
73
|
+
requireClaimed?: boolean;
|
|
74
|
+
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.resolveEscalation = resolveEscalation;
|
|
37
37
|
exports.resolveBySignalKey = resolveBySignalKey;
|
|
38
38
|
exports.resolveByIds = resolveByIds;
|
|
39
|
+
exports.resolveAllOrNone = resolveAllOrNone;
|
|
39
40
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
40
41
|
const taskService = __importStar(require("../../services/task"));
|
|
41
42
|
const escalation_strategy_1 = require("../../services/escalation-strategy");
|
|
@@ -60,7 +61,7 @@ const helpers_1 = require("./helpers");
|
|
|
60
61
|
*/
|
|
61
62
|
async function resolveEscalation(input, auth) {
|
|
62
63
|
try {
|
|
63
|
-
const { id,
|
|
64
|
+
const { id, metadata, resolverPayload } = input;
|
|
64
65
|
if (!resolverPayload) {
|
|
65
66
|
return { status: 400, error: 'resolverPayload is required' };
|
|
66
67
|
}
|
|
@@ -82,6 +83,9 @@ async function resolveEscalation(input, auth) {
|
|
|
82
83
|
const denied = await (0, helpers_1.assertWriteAccess)(auth.userId, escalation);
|
|
83
84
|
if (denied)
|
|
84
85
|
return denied;
|
|
86
|
+
// The resolver payload is stored exactly as submitted — its shape is the
|
|
87
|
+
// workflow's contract, formed by the caller (the React app maps the form to
|
|
88
|
+
// the payload via x-lt-bind before submitting). No server-side transform.
|
|
85
89
|
// `metadata` (the outcome patch) is never written separately — it rides WITH the
|
|
86
90
|
// resolverPayload to whichever path performs the resolve, so it merges inside the
|
|
87
91
|
// single status='pending'-guarded UPDATE (HotMesh `resolve(metadata)`). Paths that
|
|
@@ -133,7 +137,7 @@ async function resolveEscalation(input, auth) {
|
|
|
133
137
|
*/
|
|
134
138
|
async function resolveBySignalKey(input, auth) {
|
|
135
139
|
try {
|
|
136
|
-
const { signalKey,
|
|
140
|
+
const { signalKey, metadata, resolverPayload } = input;
|
|
137
141
|
if (!signalKey)
|
|
138
142
|
return { status: 400, error: 'signalKey is required' };
|
|
139
143
|
if (!resolverPayload)
|
|
@@ -147,6 +151,8 @@ async function resolveBySignalKey(input, auth) {
|
|
|
147
151
|
const denied = await (0, helpers_1.assertWriteAccess)(auth.userId, escalation);
|
|
148
152
|
if (denied)
|
|
149
153
|
return { status: 404, error: 'Escalation not found' };
|
|
154
|
+
// The payload is delivered as the signal the parked workflow's condition()
|
|
155
|
+
// receives — stored exactly as submitted (the caller formed its shape).
|
|
150
156
|
return resolveViaSignalKey(escalation, resolverPayload, metadata);
|
|
151
157
|
}
|
|
152
158
|
catch (err) {
|
|
@@ -166,12 +172,15 @@ async function resolveBySignalKey(input, auth) {
|
|
|
166
172
|
*/
|
|
167
173
|
async function resolveByIds(input, auth) {
|
|
168
174
|
try {
|
|
169
|
-
const { ids,
|
|
175
|
+
const { ids, metadata } = input;
|
|
170
176
|
if (!Array.isArray(ids) || ids.length === 0) {
|
|
171
177
|
return { status: 400, error: 'ids must be a non-empty array' };
|
|
172
178
|
}
|
|
173
|
-
if (!resolverPayload)
|
|
179
|
+
if (!input.resolverPayload)
|
|
174
180
|
return { status: 400, error: 'resolverPayload is required' };
|
|
181
|
+
// One pre-shaped payload for a homogeneous group of bookkeeping rows, stored
|
|
182
|
+
// as submitted — the caller forms the payload's shape.
|
|
183
|
+
const resolverPayload = input.resolverPayload;
|
|
175
184
|
// Bulk resolve ("ack") is a write verb, gated PER ITEM: write_all roles may
|
|
176
185
|
// resolve any item in the role; write_self roles may resolve only items assigned
|
|
177
186
|
// to the caller (same rule single-resolve applies via assertWriteAccess). Global
|
|
@@ -194,6 +203,103 @@ async function resolveByIds(input, auth) {
|
|
|
194
203
|
return { status: 500, error: err.message };
|
|
195
204
|
}
|
|
196
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Atomic all-or-none bulk resolve with PER-ROW payloads — the gang-handoff
|
|
208
|
+
* sibling of {@link resolveByIds}. Every listed escalation must be pending and
|
|
209
|
+
* writable by the caller, or NOTHING resolves. Unlike resolveByIds, rows
|
|
210
|
+
* backing a live `condition()` waiter are first-class: each is woken with its
|
|
211
|
+
* own payload inside the single atomic statement (same wake contract as
|
|
212
|
+
* single resolve).
|
|
213
|
+
*
|
|
214
|
+
* RBAC matches resolveByIds: per-item write scope, 404 non-disclosure when any
|
|
215
|
+
* id is missing or out of scope, nothing resolved. `requireClaimed` adds a
|
|
216
|
+
* SQL-level assertion that every row is currently assigned to the caller —
|
|
217
|
+
* for claim-then-resolve flows (a broker resolving its claimed gang), this
|
|
218
|
+
* closes the window where another principal re-claims a member mid-flight.
|
|
219
|
+
*
|
|
220
|
+
* Rows whose resolution requires a non-atomic path (legacy signal routing via
|
|
221
|
+
* `metadata.signal_id` / `metadata.signal_routing`, which need a separate
|
|
222
|
+
* workflow signal) block the batch with 409 — bulk-resolving them would strand
|
|
223
|
+
* their workflows. Re-run/triage rows (workflow_type without any signal shape)
|
|
224
|
+
* resolve as bookkeeping acknowledgements, matching resolveByIds semantics.
|
|
225
|
+
*/
|
|
226
|
+
async function resolveAllOrNone(input, auth) {
|
|
227
|
+
try {
|
|
228
|
+
const { items, metadata, requireClaimed } = input;
|
|
229
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
230
|
+
return { status: 400, error: 'items must be a non-empty array' };
|
|
231
|
+
}
|
|
232
|
+
if (items.length > defaults_1.ESCALATION_BULK_RESOLVE_MAX) {
|
|
233
|
+
return { status: 400, error: `items exceeds the maximum of ${defaults_1.ESCALATION_BULK_RESOLVE_MAX}` };
|
|
234
|
+
}
|
|
235
|
+
const wellFormed = items.every((i) => i && typeof i.id === 'string' && i.id.length > 0
|
|
236
|
+
&& i.resolverPayload && typeof i.resolverPayload === 'object');
|
|
237
|
+
if (!wellFormed) {
|
|
238
|
+
return { status: 400, error: 'every item requires an id and a resolverPayload object' };
|
|
239
|
+
}
|
|
240
|
+
const ids = items.map((i) => i.id);
|
|
241
|
+
if (new Set(ids).size !== ids.length) {
|
|
242
|
+
return { status: 400, error: 'items must not repeat ids' };
|
|
243
|
+
}
|
|
244
|
+
// One indexed read serves every pre-flight gate below (scope, path shape,
|
|
245
|
+
// redaction schema). Role and signal routing are immutable per escalation,
|
|
246
|
+
// so none of these checks race the guarded statement that follows — and
|
|
247
|
+
// row STATE (pending, assignee) is re-asserted inside that statement.
|
|
248
|
+
const rows = await escalationService.getEscalationsByIds(ids);
|
|
249
|
+
const rowById = new Map(rows.map((r) => [r.id, r]));
|
|
250
|
+
// Per-item write scope, same rule as resolveByIds: write_all roles may
|
|
251
|
+
// resolve any item in the role; write_self only items assigned to the
|
|
252
|
+
// caller. A missing id or any out-of-scope item → 404 (non-disclosure),
|
|
253
|
+
// nothing resolved. Global bypasses; the store then reports precise
|
|
254
|
+
// per-row blockers (including not-found) via the 409 below.
|
|
255
|
+
const writeScope = await (0, helpers_1.getEscalationWriteScope)(auth.userId);
|
|
256
|
+
if (!writeScope.global) {
|
|
257
|
+
const allSet = new Set(writeScope.allRoles);
|
|
258
|
+
const selfSet = new Set(writeScope.selfRoles);
|
|
259
|
+
const writable = (r) => (!!r.role && allSet.has(r.role)) ||
|
|
260
|
+
(!!r.role && selfSet.has(r.role) && r.assigned_to === auth.userId);
|
|
261
|
+
if (rows.length !== ids.length || !rows.every(writable)) {
|
|
262
|
+
return { status: 404, error: 'One or more escalations not found' };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Fail loud on rows the atomic statement cannot wake: legacy signal shapes
|
|
266
|
+
// resume their workflow via a separate handle.signal, so bulk-resolving the
|
|
267
|
+
// row would mark it settled while the workflow stays parked forever.
|
|
268
|
+
const unsupported = rows.filter((r) => {
|
|
269
|
+
const meta = (r.metadata ?? {});
|
|
270
|
+
return !r.signal_key && (meta.signal_id || meta.signal_routing?.signalId);
|
|
271
|
+
});
|
|
272
|
+
if (unsupported.length > 0) {
|
|
273
|
+
const failed = unsupported.map((r) => ({ id: r.id, reason: 'unsupported-resolution-path' }));
|
|
274
|
+
// error rides inside data too: routes serialize `data ?? { error }`, and
|
|
275
|
+
// the 409 body must carry BOTH the message and the blocking ids
|
|
276
|
+
const error = 'One or more escalations require the single-resolve path (legacy signal routing)';
|
|
277
|
+
return { status: 409, error, data: { error, failedIds: failed.map((f) => f.id), failed } };
|
|
278
|
+
}
|
|
279
|
+
// Per-row password redaction against each row's own form schema — the
|
|
280
|
+
// payload enters the signal store and the audit record; plaintext never.
|
|
281
|
+
const serviceItems = await Promise.all(items.map(async (item) => ({
|
|
282
|
+
id: item.id,
|
|
283
|
+
resolverPayload: await redactPasswords(item.resolverPayload, rowById.get(item.id)?.metadata?.form_schema),
|
|
284
|
+
})));
|
|
285
|
+
const result = await escalationService.resolveEscalationsAllOrNone(serviceItems, metadata, requireClaimed ? auth.userId : undefined);
|
|
286
|
+
if (!result.ok) {
|
|
287
|
+
const error = 'One or more escalations blocked the batch; nothing was resolved';
|
|
288
|
+
return {
|
|
289
|
+
status: 409,
|
|
290
|
+
error,
|
|
291
|
+
data: { error, failedIds: result.failed.map((f) => f.id), failed: result.failed },
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
status: 200,
|
|
296
|
+
data: { resolved: result.escalations.length, escalationIds: result.escalations.map((e) => e.id) },
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
return { status: 500, error: err.message };
|
|
301
|
+
}
|
|
302
|
+
}
|
|
197
303
|
// ── Resolution paths ─────────────────────────────────────────────────────
|
|
198
304
|
/** Path A: lightweight conditionLT signal — inject $escalation_id and signal the running workflow. */
|
|
199
305
|
async function resolveViaConditionSignal(escalation, resolverPayload, metadata) {
|
|
@@ -51,14 +51,18 @@ const helpers_1 = require("./helpers");
|
|
|
51
51
|
*/
|
|
52
52
|
async function getEscalation(input, auth) {
|
|
53
53
|
try {
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// One query: the escalation plus its role's form, resolved to the version the
|
|
55
|
+
// row pinned (or the role's latest) and embedded as `form_schema`. The resolve
|
|
56
|
+
// UI renders from this without a second schema call, and the form is never
|
|
57
|
+
// stored on the row — it is JOINed in from the roles tables.
|
|
58
|
+
const detail = await escalationService.getEscalationWithFormSchema(input.id);
|
|
59
|
+
if (!detail) {
|
|
56
60
|
return { status: 404, error: 'Escalation not found' };
|
|
57
61
|
}
|
|
58
|
-
const denied = await (0, helpers_1.assertReadAccess)(auth.userId, escalation);
|
|
62
|
+
const denied = await (0, helpers_1.assertReadAccess)(auth.userId, detail.escalation);
|
|
59
63
|
if (denied)
|
|
60
64
|
return denied;
|
|
61
|
-
return { status: 200, data: escalation };
|
|
65
|
+
return { status: 200, data: { ...detail.escalation, form_schema: detail.form_schema } };
|
|
62
66
|
}
|
|
63
67
|
catch (err) {
|
|
64
68
|
return { status: 500, error: err.message };
|
package/build/api/roles.d.ts
CHANGED
|
@@ -83,6 +83,7 @@ export declare function updateRole(input: {
|
|
|
83
83
|
role: string;
|
|
84
84
|
title?: string | null;
|
|
85
85
|
description?: string | null;
|
|
86
|
+
/** The escalation resolve FORM schema (the JIT UI). Fields may carry x-lt-bind. */
|
|
86
87
|
form_schema?: Record<string, any> | null;
|
|
87
88
|
metadata_schema?: Record<string, any> | null;
|
|
88
89
|
properties?: Record<string, any> | null;
|
package/build/lib/db/index.js
CHANGED
|
@@ -8,7 +8,18 @@ const config_1 = require("../../modules/config");
|
|
|
8
8
|
let pool = null;
|
|
9
9
|
function getPool() {
|
|
10
10
|
if (!pool) {
|
|
11
|
-
pool = new pg_1.Pool(
|
|
11
|
+
pool = new pg_1.Pool({
|
|
12
|
+
...config_1.postgres_options,
|
|
13
|
+
// Long-tail's shared tables live in public. Postgres's default
|
|
14
|
+
// search_path is `"$user", public`: if any schema shares the connecting
|
|
15
|
+
// role's name (e.g. a HotMesh app schema named after the DB user), every
|
|
16
|
+
// unqualified statement — including CREATE TABLE in migrations — silently
|
|
17
|
+
// resolves there instead of public, forking the data across two schemas.
|
|
18
|
+
// Pinning the session search_path makes resolution independent of role
|
|
19
|
+
// and schema naming. HotMesh connections (getConnection) are unaffected:
|
|
20
|
+
// the engine fully qualifies its app schemas.
|
|
21
|
+
options: '-c search_path=public',
|
|
22
|
+
});
|
|
12
23
|
}
|
|
13
24
|
return pool;
|
|
14
25
|
}
|
package/build/lib/db/migrate.js
CHANGED
|
@@ -47,7 +47,34 @@ async function migrate() {
|
|
|
47
47
|
// Uses a dedicated client so the lock is held for the entire sequence.
|
|
48
48
|
const client = await pool.connect();
|
|
49
49
|
try {
|
|
50
|
+
// Migrations define the shared public tables, so resolution is pinned on
|
|
51
|
+
// this client even if a future caller wires a pool without the pool-level
|
|
52
|
+
// search_path option. Without this, a schema named after the DB role
|
|
53
|
+
// (default search_path `"$user", public`) captures every unqualified
|
|
54
|
+
// CREATE TABLE below.
|
|
55
|
+
await client.query('SET search_path TO public');
|
|
50
56
|
await client.query('SELECT pg_advisory_lock(8675309)');
|
|
57
|
+
// Fail loudly on a pre-existing split: an lt_migrations table in any other
|
|
58
|
+
// schema means a prior boot ran with `"$user"` shadowing public and forked
|
|
59
|
+
// the tables. Booting through it would either fork the data further or
|
|
60
|
+
// re-run every migration against empty public tables while the live rows
|
|
61
|
+
// sit in the shadow schema. An operator must consolidate first (move the
|
|
62
|
+
// live lt_* tables to public, archive the stale copies).
|
|
63
|
+
const drift = await client.query(`
|
|
64
|
+
SELECT n.nspname
|
|
65
|
+
FROM pg_class c
|
|
66
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
67
|
+
WHERE c.relname = 'lt_migrations' AND c.relkind = 'r' AND n.nspname <> 'public'
|
|
68
|
+
`);
|
|
69
|
+
if (drift.rows.length > 0) {
|
|
70
|
+
const schemas = drift.rows.map((r) => r.nspname).join(', ');
|
|
71
|
+
throw new Error(`[migrate] lt_migrations exists in non-public schema(s): ${schemas}. ` +
|
|
72
|
+
'Long-tail tables must live in the public schema. This usually means the ' +
|
|
73
|
+
'database user shares a name with another schema, so the default ' +
|
|
74
|
+
'search_path ("$user", public) captured earlier migrations. Consolidate ' +
|
|
75
|
+
'the schemas (ALTER TABLE ... SET SCHEMA public for the live lt_* tables, ' +
|
|
76
|
+
'archive stale public copies) before starting the app.');
|
|
77
|
+
}
|
|
51
78
|
// ensure migration tracking table
|
|
52
79
|
await client.query(`
|
|
53
80
|
CREATE TABLE IF NOT EXISTS lt_migrations (
|
|
@@ -49,6 +49,8 @@ export declare const QUERY_LIMIT_MAX: number;
|
|
|
49
49
|
export declare const YAML_LIST_LIMIT: number;
|
|
50
50
|
/** Default page size for YAML workflow version history. */
|
|
51
51
|
export declare const YAML_VERSION_LIMIT: number;
|
|
52
|
+
/** Maximum items per atomic bulk resolve (POST /escalations/resolve-all-or-none). */
|
|
53
|
+
export declare const ESCALATION_BULK_RESOLVE_MAX: number;
|
|
52
54
|
/**
|
|
53
55
|
* Claim duration presets shown in the dashboard (minutes).
|
|
54
56
|
* Override via JSON array, e.g. LT_CLAIM_DURATION_OPTIONS='[15,30,60,480]'
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Grouped by domain so related knobs live together.
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.YAML_LINE_WIDTH = exports.STOP_WORDS = exports.CLAIM_DURATION_OPTIONS = exports.YAML_VERSION_LIMIT = exports.YAML_LIST_LIMIT = exports.QUERY_LIMIT_MAX = exports.QUERY_LIMIT_DEFAULT = exports.CONFIG_CACHE_TTL_MS = exports.MCP_TOOL_TIMEOUT_MS = exports.WORKFLOW_SYNC_TIMEOUT_MS = exports.JOB_EXPIRE_SECS = exports.WORKFLOW_EXPIRE_SECS = exports.TOOL_ARG_LIMIT_CAP = exports.TOOL_ROUNDS_MCP_QUERY = exports.TOOL_ROUNDS_TRIAGE = exports.TOOL_RESULT_MAX_CHARS = exports.LLM_MAX_INPUT_CHARS = exports.LLM_MAX_ARRAY_ITEMS = exports.LLM_MAX_TOKENS_VISION = exports.LLM_MAX_TOKENS_JSON = exports.LLM_MAX_TOKENS_DEFAULT = exports.LLM_BASE_URL = exports.LLM_MODEL_SECONDARY = exports.LLM_MODEL_PRIMARY = void 0;
|
|
12
|
+
exports.YAML_LINE_WIDTH = exports.STOP_WORDS = exports.CLAIM_DURATION_OPTIONS = exports.ESCALATION_BULK_RESOLVE_MAX = exports.YAML_VERSION_LIMIT = exports.YAML_LIST_LIMIT = exports.QUERY_LIMIT_MAX = exports.QUERY_LIMIT_DEFAULT = exports.CONFIG_CACHE_TTL_MS = exports.MCP_TOOL_TIMEOUT_MS = exports.WORKFLOW_SYNC_TIMEOUT_MS = exports.JOB_EXPIRE_SECS = exports.WORKFLOW_EXPIRE_SECS = exports.TOOL_ARG_LIMIT_CAP = exports.TOOL_ROUNDS_MCP_QUERY = exports.TOOL_ROUNDS_TRIAGE = exports.TOOL_RESULT_MAX_CHARS = exports.LLM_MAX_INPUT_CHARS = exports.LLM_MAX_ARRAY_ITEMS = exports.LLM_MAX_TOKENS_VISION = exports.LLM_MAX_TOKENS_JSON = exports.LLM_MAX_TOKENS_DEFAULT = exports.LLM_BASE_URL = exports.LLM_MODEL_SECONDARY = exports.LLM_MODEL_PRIMARY = void 0;
|
|
13
13
|
function envInt(key, fallback) {
|
|
14
14
|
const v = process.env[key];
|
|
15
15
|
if (v === undefined || v === '')
|
|
@@ -67,6 +67,8 @@ exports.YAML_LIST_LIMIT = envInt('LT_YAML_LIST_LIMIT', 50);
|
|
|
67
67
|
/** Default page size for YAML workflow version history. */
|
|
68
68
|
exports.YAML_VERSION_LIMIT = envInt('LT_YAML_VERSION_LIMIT', 20);
|
|
69
69
|
// ── Escalation ──────────────────────────────────────────────────────
|
|
70
|
+
/** Maximum items per atomic bulk resolve (POST /escalations/resolve-all-or-none). */
|
|
71
|
+
exports.ESCALATION_BULK_RESOLVE_MAX = envInt('LT_ESCALATION_BULK_RESOLVE_MAX', 100);
|
|
70
72
|
/**
|
|
71
73
|
* Claim duration presets shown in the dashboard (minutes).
|
|
72
74
|
* Override via JSON array, e.g. LT_CLAIM_DURATION_OPTIONS='[15,30,60,480]'
|
|
@@ -66,6 +66,24 @@ function registerResolveRoutes(router) {
|
|
|
66
66
|
}, req.auth);
|
|
67
67
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
68
68
|
});
|
|
69
|
+
/**
|
|
70
|
+
* POST /api/escalations/resolve-all-or-none
|
|
71
|
+
* Atomic bulk resolve with per-row payloads: every listed escalation resolves
|
|
72
|
+
* with its own resolverPayload in one statement, or nothing resolves. Rows
|
|
73
|
+
* backing a live condition() waiter are woken with their own payload (same
|
|
74
|
+
* wake contract as /:id/resolve). 409 returns { error, failedIds, failed }
|
|
75
|
+
* naming exactly the rows that blocked the batch. Max 100 items per call.
|
|
76
|
+
* Body: { items: Array<{ id: string, resolverPayload: Record<string, any> }>,
|
|
77
|
+
* metadata?: Record<string, any>, requireClaimed?: boolean }
|
|
78
|
+
*/
|
|
79
|
+
router.post('/resolve-all-or-none', async (req, res) => {
|
|
80
|
+
const result = await api.resolveAllOrNone({
|
|
81
|
+
items: req.body?.items,
|
|
82
|
+
metadata: req.body?.metadata,
|
|
83
|
+
requireClaimed: req.body?.requireClaimed,
|
|
84
|
+
}, req.auth);
|
|
85
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
86
|
+
});
|
|
69
87
|
/**
|
|
70
88
|
* POST /api/escalations/:id/resolve
|
|
71
89
|
* Resolve a pending escalation with a human-provided payload. Routes by
|
|
@@ -3,16 +3,19 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Spins up the whole DIGITAL side of the physical/digital twin farm from one
|
|
5
5
|
* command and plays every human part automatically, so a single run exercises
|
|
6
|
-
* the full lifecycle: register → advertise → dispatch → print →
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* the full lifecycle: onboard (register + bind) → advertise → dispatch → print →
|
|
7
|
+
* reconcile → settle, plus the failure detours (autonomous fail → inspect, and
|
|
8
|
+
* filament runout → change → resume). The PHYSICAL side is the app's Bambu
|
|
9
|
+
* backend — `BAMBU_BACKEND=mock` by default (a deterministic in-repo simulation),
|
|
10
|
+
* or `http` against a real Farm Manager Server once the office link is wired.
|
|
11
|
+
*
|
|
12
|
+
* Faults are injected deterministically via each order unit's `simOutcome` (the
|
|
13
|
+
* mock honors it), so the service escalations fire on demand — no racing.
|
|
10
14
|
*
|
|
11
15
|
* Usage:
|
|
12
16
|
* npm run twin:farm
|
|
13
17
|
* FLEET=6 ORDERS=10 UNITS=2 npm run twin:farm
|
|
14
|
-
* FAIL_PCT=
|
|
15
|
-
* FARM_MANAGER_URL=http://192.168.1.50:4000 npm run twin:farm # preflight the office link
|
|
18
|
+
* FAIL_PCT=30 FAIL_MODE=filament_runout npm run twin:farm # fault → service → recover
|
|
16
19
|
* FLEET=4 ORDERS=4 KEEP=1 npm run twin:farm # leave twins running to poke in the UI
|
|
17
20
|
*
|
|
18
21
|
* Env vars:
|
|
@@ -24,17 +27,13 @@
|
|
|
24
27
|
* FILAMENTS comma-separated filament pool the fleet + orders draw from
|
|
25
28
|
* (default "pla,petg")
|
|
26
29
|
* ARRIVAL_S average seconds between order arrivals (default 2)
|
|
27
|
-
* FAIL_PCT percent of
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* a wider window (see README).
|
|
31
|
-
* POLL_MS poll interval ms (default 750)
|
|
30
|
+
* FAIL_PCT percent of orders with one faulted unit (default 0)
|
|
31
|
+
* FAIL_MODE the injected fault: filament_runout | failed (default filament_runout)
|
|
32
|
+
* POLL_MS servicer/registrar poll interval ms (default 750)
|
|
32
33
|
* BROKER_IDLE idle broker ticks before it self-terminates (default 40)
|
|
33
34
|
* RUN_BROKER launch a broker (default 1; set 0 if one already runs)
|
|
34
35
|
* KEEP 1 = leave twins advertising at the end (default 0 = retire
|
|
35
36
|
* them with a power-down so nothing lingers between runs)
|
|
36
|
-
* FARM_MANAGER_URL optional: GET this before running and report reachability,
|
|
37
|
-
* a preflight for the office laptop → farm-manager-host link
|
|
38
37
|
*
|
|
39
38
|
* Capabilities: each twin is registered with a capability profile (xl/pdac/soft).
|
|
40
39
|
* By default the fleet is fully capable and only filament routes; set FLEET_CAPS
|