@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
|
@@ -140,7 +140,7 @@ PUT /api/workflows/:type/config
|
|
|
140
140
|
| `execute_as` | `string \| null` | `null` | Service account `external_id` to run as (overrides invoker identity) |
|
|
141
141
|
| `tool_tags` | `string[]` | `[]` | MCP tool tags for scoped tool discovery |
|
|
142
142
|
| `envelope_schema` | `object \| null` | `null` | JSON Schema for the workflow input envelope |
|
|
143
|
-
| `resolver_schema` | `object \| null` | `null` |
|
|
143
|
+
| `resolver_schema` | `object \| null` | `null` | **Deprecated** legacy fallback only. The escalation form is owned by the target role as a versioned `form_schema`. |
|
|
144
144
|
| `cron_schedule` | `string \| null` | `null` | Cron expression for scheduled execution (e.g., `"0 9 * * *"`) |
|
|
145
145
|
|
|
146
146
|
**Example request:**
|
|
@@ -387,7 +387,7 @@ export async function stationWorker(envelope: LTEnvelope) {
|
|
|
387
387
|
workflowType: 'stationWorker',
|
|
388
388
|
metadata: { orderId: envelope.data.orderId, station: 'qc' },
|
|
389
389
|
envelope: { instructions: 'Review and approve or reject' },
|
|
390
|
-
timeout: '24h', // SLA for this worklist row
|
|
390
|
+
timeout: '24h', // SLA for this worklist row
|
|
391
391
|
});
|
|
392
392
|
|
|
393
393
|
if (decision === false) {
|
|
@@ -405,7 +405,7 @@ export async function stationWorker(envelope: LTEnvelope) {
|
|
|
405
405
|
|
|
406
406
|
### SLA-gated waits (`timeout`)
|
|
407
407
|
|
|
408
|
-
`ConditionQueueConfig.timeout`
|
|
408
|
+
`ConditionQueueConfig.timeout` arms the same resume timer as
|
|
409
409
|
`condition(signalId, '24h')` **in the same single Leg1 write** that creates the
|
|
410
410
|
escalation row. The race is resolved atomically on both sides: a signal that
|
|
411
411
|
arrives first resolves the row normally and the timer is inert; a timer that
|
|
@@ -472,9 +472,9 @@ If you use raw `Durable.workflow.condition()` instead, the `$escalation_id` fiel
|
|
|
472
472
|
|
|
473
473
|
When a reviewer claims an escalation in the dashboard, a typed form is rendered instead of a raw JSON editor — if a schema is available. There are two ways to provide one:
|
|
474
474
|
|
|
475
|
-
**Option 1 —
|
|
475
|
+
**Option 1 — Role `form_schema` (versioned):** The escalation form is owned by the target role as a versioned `form_schema`. A workflow pins a version through `conditionLT`'s `schemaVersion`; unpinned escalations resolve against the role's latest. Fields may carry `x-lt-bind` to map a form value to a path in the resolver payload. The deprecated workflow-config `resolver_schema` remains only as a legacy fallback.
|
|
476
476
|
|
|
477
|
-
**Option 2 — Escalation metadata (dynamic):** Pass `form_schema` inside `metadata` when creating an escalation. This overrides any
|
|
477
|
+
**Option 2 — Escalation metadata (dynamic):** Pass `form_schema` inside `metadata` when creating an escalation. This overrides any role-level schema.
|
|
478
478
|
|
|
479
479
|
### Schema shape
|
|
480
480
|
|
|
@@ -511,7 +511,7 @@ Keys prefixed with `_` are stored in the payload but hidden from the form. `_for
|
|
|
511
511
|
|
|
512
512
|
### Priority
|
|
513
513
|
|
|
514
|
-
`metadata.form_schema`
|
|
514
|
+
The form is resolved by precedence: `metadata.form_schema` (legacy inline on the row) > the role's `form_schema` (resolved to the escalation's pinned `metadata.schema_version`, or the role's latest when unpinned) > the deprecated workflow-config `resolver_schema` legacy fallback.
|
|
515
515
|
|
|
516
516
|
---
|
|
517
517
|
|
|
@@ -823,6 +823,41 @@ const result = await lt.escalations.resolveByIds({
|
|
|
823
823
|
|
|
824
824
|
**Returns:** `LTApiResult<{ resolved: number; escalationIds: string[] }>` — only still-`pending` rows are resolved.
|
|
825
825
|
|
|
826
|
+
## resolveAllOrNone
|
|
827
|
+
|
|
828
|
+
Atomic bulk resolve with per-row payloads: every listed escalation resolves with its own `resolverPayload` in one SQL statement, or nothing resolves. Rows backing a live `condition()` waiter are first-class — each waiter's wake commits with its resolve, delivering that row's payload as the condition's return value (the same wake contract as `resolve`). Use it for gang handoffs where each member must receive a distinct mandate and a partial batch is unacceptable.
|
|
829
|
+
|
|
830
|
+
RBAC matches `resolveByIds`: per-item write scope, and any missing or out-of-scope id returns 404 with nothing resolved. `requireClaimed` additionally asserts — inside the same guarded statement — that every row is currently assigned to the caller, closing the window where another principal re-claims a member between the caller's claim and the resolve.
|
|
831
|
+
|
|
832
|
+
```typescript
|
|
833
|
+
const result = await lt.escalations.resolveAllOrNone({
|
|
834
|
+
items: [
|
|
835
|
+
{ id: 'esc_left', resolverPayload: { gcodeRef: 'gcode-left', unit: 'left' } },
|
|
836
|
+
{ id: 'esc_right', resolverPayload: { gcodeRef: 'gcode-right', unit: 'right' } },
|
|
837
|
+
],
|
|
838
|
+
metadata: { outcome: 'gang-dispatched' },
|
|
839
|
+
requireClaimed: true,
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
if (result.status === 409) {
|
|
843
|
+
// nothing resolved — result.data.failed names exactly the blocking rows
|
|
844
|
+
// (not-found | already-resolved | already-cancelled | already-expired |
|
|
845
|
+
// assignee-mismatch | unsupported-resolution-path); re-gang around them
|
|
846
|
+
}
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
| Field | Type | Required | Description |
|
|
850
|
+
|-------|------|----------|-------------|
|
|
851
|
+
| `items` | `Array<{ id, resolverPayload }>` | Yes | The batch — each row resolves with its own payload. Ids must be unique; max 100 items (`LT_ESCALATION_BULK_RESOLVE_MAX`) |
|
|
852
|
+
| `metadata` | `Record<string, any>` | No | Shared outcome patch merged into every row's GIN-indexed metadata |
|
|
853
|
+
| `requireClaimed` | `boolean` | No | Assert every row is currently assigned to the caller, inside the atomic statement |
|
|
854
|
+
|
|
855
|
+
**Returns:** `LTApiResult<{ resolved: number; escalationIds: string[] }>` on success. On 409 the data carries `{ error, failedIds, failed: [{ id, reason }] }` — only the rows that blocked the batch are listed; resolvable members stay pending, untouched.
|
|
856
|
+
|
|
857
|
+
Rows that resolve through legacy signal routing (`metadata.signal_id` / `metadata.signal_routing`) require the single `resolve` path, which delivers their workflow signal; including one blocks the batch with reason `unsupported-resolution-path`. Password-format fields are redacted per row against that row's own `form_schema` before the payload enters the signal store.
|
|
858
|
+
|
|
859
|
+
**Auth:** Required
|
|
860
|
+
|
|
826
861
|
## resolveBySignalKey
|
|
827
862
|
|
|
828
863
|
Resolve an efficient (atomic) escalation directly by its `signal_key` and resume the waiting workflow in place. For callers that know the deterministic signal id and want to skip the id lookup. RBAC-scoped to the escalation's role.
|
|
@@ -242,7 +242,7 @@ const result = await lt.workflows.upsertConfig({
|
|
|
242
242
|
| `consumes` | `string[]` | No | Workflow types whose data this workflow consumes |
|
|
243
243
|
| `tool_tags` | `string[]` | No | MCP tool tags for discovery |
|
|
244
244
|
| `envelope_schema` | `any` | No | JSON Schema for envelope.data validation |
|
|
245
|
-
| `resolver_schema` | `any` | No |
|
|
245
|
+
| `resolver_schema` | `any` | No | **Deprecated** legacy fallback only. There is no resolver-payload validation; the escalation form is owned by the target role as a versioned `form_schema`. |
|
|
246
246
|
| `cron_schedule` | `string \| null` | No | Cron expression for scheduled execution |
|
|
247
247
|
|
|
248
248
|
**Returns:** `LTApiResult<LTWorkflowConfig>`
|
package/docs/dashboard.md
CHANGED
|
@@ -6,7 +6,7 @@ The Long Tail dashboard is a React single-page application for managing procedur
|
|
|
6
6
|
|
|
7
7
|
The sidebar organizes pages into six groups.
|
|
8
8
|
|
|
9
|
-
###
|
|
9
|
+
### Monitor
|
|
10
10
|
|
|
11
11
|
The reactive, event-driven surface — where operations watch the floor and builders configure choreography.
|
|
12
12
|
|
|
@@ -110,7 +110,7 @@ workers: [
|
|
|
110
110
|
certified: true,
|
|
111
111
|
roles: ['reviewer', 'admin'],
|
|
112
112
|
envelopeSchema: { data: { field1: '', field2: 0 } },
|
|
113
|
-
resolverSchema: { approved: true, notes: '' },
|
|
113
|
+
resolverSchema: { approved: true, notes: '' }, // deprecated legacy fallback — the escalation form is owned by the target role as a versioned form_schema
|
|
114
114
|
},
|
|
115
115
|
},
|
|
116
116
|
]
|
|
@@ -308,7 +308,7 @@ The central queue for all escalation activity across every workflow.
|
|
|
308
308
|
- **Filter bar** — filter by status (pending/claimed/resolved), role, workflow type, priority, and time window.
|
|
309
309
|
- **Columns:** Escalation ID, workflow type, role, status, priority, created time, and claimed-by user.
|
|
310
310
|
- **Claim** — click the claim action to lock an escalation to your user. Only users with matching roles see pending escalations. The queue list and aggregate stats reflect `read_all` memberships — a member scoped to `read_self` lands directly on their own assigned item in user mode rather than browsing the full queue.
|
|
311
|
-
- **Resolve** — after claiming, submit a resolver payload
|
|
311
|
+
- **Resolve** — after claiming, submit a resolver payload. The form is pre-filled from the role's versioned `form_schema` field defaults and from the workflow's seeded `envelope.formDefaults` (reverse-mapped through each field's `x-lt-bind`). The dashboard maps the flat form to the nested payload via `x-lt-bind` and the submitted payload is stored as-is. Resolution triggers a workflow re-run with the resolver data injected. A `member` whose `write_scope` is `self` can resolve only items already assigned to them; `write_scope=none` is read-only.
|
|
312
312
|
- **Escalate** — forward a claimed escalation to a higher-tier role via the escalation chain.
|
|
313
313
|
|
|
314
314
|
**API:** `GET /api/escalations` lists with filters. `POST /api/escalations/:id/claim` claims. `POST /api/escalations/:id/resolve` resolves.
|
package/docs/data.md
CHANGED
|
@@ -104,7 +104,7 @@ Records human intervention requests. Created when a workflow returns
|
|
|
104
104
|
workflow calls `condition(signalId, config)` / `conditionLT(signalId, config)`.
|
|
105
105
|
Updated when claimed or resolved.
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
The storage is the **shared HotMesh table `public.hmsh_escalations`**
|
|
108
108
|
(written by both long-tail and the HotMesh SDK). `lt_escalations` remains as a
|
|
109
109
|
backward-compatible **view** over it — `SELECT *` plus a computed `available`
|
|
110
110
|
column — so existing read queries and the public API are unchanged. Indexes are
|
|
@@ -263,7 +263,7 @@ Workflow registration. Every workflow that uses the LT interceptor must have a r
|
|
|
263
263
|
| `consumes` | `TEXT[] NOT NULL` | `'{}'` | Array of workflow types whose completed data this workflow receives via `envelope.lt.providers` |
|
|
264
264
|
| `tool_tags` | `TEXT[]` | `'{}'` | Tags for MCP tool discovery |
|
|
265
265
|
| `envelope_schema` | `JSONB` | — | JSON Schema for the workflow input envelope |
|
|
266
|
-
| `resolver_schema` | `JSONB` | — |
|
|
266
|
+
| `resolver_schema` | `JSONB` | — | **Deprecated** legacy fallback only. The escalation form is owned by the target role as a versioned `form_schema`. |
|
|
267
267
|
| `cron_schedule` | `TEXT` | — | Cron expression for scheduled execution |
|
|
268
268
|
| `created_at` | `TIMESTAMPTZ NOT NULL` | `NOW()` | Row creation time |
|
|
269
269
|
| `updated_at` | `TIMESTAMPTZ NOT NULL` | `NOW()` | Last modification |
|
package/docs/hitl-guide.md
CHANGED
|
@@ -83,9 +83,24 @@ export async function approvalWorkflow(envelope: LTEnvelope) {
|
|
|
83
83
|
}
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
The `timeout` field
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
The `timeout` field makes the wait SLA-gated in the same single Leg1 write:
|
|
87
|
+
one `conditionLT` call yields the worklist row AND the resume timer. Omit it
|
|
88
|
+
for an open-ended wait.
|
|
89
|
+
|
|
90
|
+
Two engine contracts worth building on:
|
|
91
|
+
|
|
92
|
+
- **The row is complete from its first visible moment.** Every field of the
|
|
93
|
+
config — including `metadata` facets — commits inside the Leg1 checkpoint.
|
|
94
|
+
A claim-by-metadata router or a version-pinned facet (e.g. a
|
|
95
|
+
`schema_version` the resolver UI renders) can trust every row it reads;
|
|
96
|
+
there is no window where a row is visible but its metadata is still en route.
|
|
97
|
+
- **Early signals are buffered.** A resolve that races ahead of the
|
|
98
|
+
`condition()` registration (a fast webhook, a payload deposited before the
|
|
99
|
+
workflow starts) is held as a pending signal and delivered when the wait
|
|
100
|
+
registers — 10 minutes by default; pass `expire` to `signal()` (e.g. `'1h'`)
|
|
101
|
+
when signaling early on purpose. Fan-out (`Promise.all` over many waits)
|
|
102
|
+
scales the same way: buffering covers every signal that outruns its
|
|
103
|
+
registration.
|
|
89
104
|
|
|
90
105
|
#### Two-step form
|
|
91
106
|
|
|
@@ -165,9 +180,8 @@ export async function reviewWorkflow(envelope: LTEnvelope) {
|
|
|
165
180
|
The resolver form resolves in order, most specific first:
|
|
166
181
|
|
|
167
182
|
1. **`metadata.form_schema`** — a full JSON Schema embedded on the escalation row. Use when different escalation points in the same workflow need different forms.
|
|
168
|
-
2.
|
|
169
|
-
3. **Workflow config `resolver_schema`** —
|
|
170
|
-
4. **The role's latest `form_schema`** — the fallback every role provides.
|
|
183
|
+
2. **The role's `form_schema`** — the versioned form every role provides. When the row carries a `metadata.schema_version` pin (set via `schemaVersion` in the `conditionLT` config), the form renders exactly that snapshot even after the role's schema changes; without a pin, the role's latest applies.
|
|
184
|
+
3. **Workflow config `resolver_schema`** — **deprecated** legacy fallback only, used when no role `form_schema` is available.
|
|
171
185
|
|
|
172
186
|
### Versioned Role Schemas
|
|
173
187
|
|
|
@@ -210,12 +224,47 @@ await lt.escalations.resolve({
|
|
|
210
224
|
|
|
211
225
|
The patch is distinct from the resolver payload: the payload resumes the paused workflow and is not indexed; the metadata patch is the durable, queryable record on the row. Use it for the audit trail and analytics — disposition, reviewer, time-to-resolve — so the escalation table answers *what was asked, what was decided, and how long it took* without a parallel log.
|
|
212
226
|
|
|
227
|
+
### Resolving a set atomically
|
|
228
|
+
|
|
229
|
+
When one decision settles a SET of waits — each with its own payload — use
|
|
230
|
+
`lt.escalations.resolveAllOrNone({ items })` (`POST /api/escalations/resolve-all-or-none`).
|
|
231
|
+
Every listed row resolves with its own `resolverPayload` in one SQL statement,
|
|
232
|
+
waking each parked workflow with its own value, or nothing resolves and the
|
|
233
|
+
409 body names exactly the rows that blocked (`failedIds` + reasons). Pass
|
|
234
|
+
`requireClaimed: true` in claim-then-resolve flows to assert, inside the same
|
|
235
|
+
statement, that every row is still assigned to the caller. See the
|
|
236
|
+
[SDK reference](./api/sdk/escalations.md#resolveallornone) for the full contract.
|
|
237
|
+
|
|
213
238
|
---
|
|
214
239
|
|
|
215
240
|
## JSON Schema Form Authoring
|
|
216
241
|
|
|
217
242
|
The dashboard renders forms automatically from JSON Schema. No frontend code needed.
|
|
218
243
|
|
|
244
|
+
The full custom vocabulary at a glance — every `x-lt-*` keyword and extension key the renderer honors:
|
|
245
|
+
|
|
246
|
+
| Keyword | Level | Purpose |
|
|
247
|
+
|---------|-------|---------|
|
|
248
|
+
| `x-lt-widget` | field | Rich control: `file-upload`, `code-editor`, `signature`, `rich-text`, `markdown` |
|
|
249
|
+
| `x-lt-language` | field | Syntax hint shown by the `code-editor` widget |
|
|
250
|
+
| `accept` | field | File-type filter for the `file-upload` widget (e.g. `".pdf,.png"`) |
|
|
251
|
+
| `x-lt-bind` | field | Path this field's value occupies in the resolver payload (e.g. `"customer.email"`) |
|
|
252
|
+
| `x-lt-span` | field | Column span in a `two-column` layout (`2` = full width) |
|
|
253
|
+
| `x-lt-order` | schema | Field render sequence |
|
|
254
|
+
| `x-lt-layout` | schema | `"two-column"` grid layout |
|
|
255
|
+
| `x-lt-context` | schema | Context panel text shown alongside the form in user mode |
|
|
256
|
+
| `x-lt-viewport` | schema | Replace the generated form with a custom iframe UI |
|
|
257
|
+
| `format` | field | Input specialization: `password`, `date`, `date-time`, `email`, `uri`, `textarea` |
|
|
258
|
+
| `readOnly` | field | Static display (or a rendered content block with the `markdown` widget) |
|
|
259
|
+
| `required` | schema | Fields that must be filled before submit |
|
|
260
|
+
| `title` / `description` | both | Section header / helper text |
|
|
261
|
+
|
|
262
|
+
The working reference is `examples/workflows/rich-form/` — the `intake-reviewer` role's
|
|
263
|
+
versioned `form_schema` (seeded by `examples/seed-rich-form.ts`) exercises the whole
|
|
264
|
+
vocabulary in one form: a markdown content block, two-column layout, ordering, date and
|
|
265
|
+
email formats, enum, file upload, spans, required fields, and `x-lt-bind` mapping into a
|
|
266
|
+
nested payload.
|
|
267
|
+
|
|
219
268
|
### Supported Field Types
|
|
220
269
|
|
|
221
270
|
| JSON Type | Renders As |
|
|
@@ -261,6 +310,7 @@ For rich inputs beyond standard HTML types:
|
|
|
261
310
|
| `"code-editor"` | Monospace textarea with tab-key support. Use `x-lt-language` for syntax hint. |
|
|
262
311
|
| `"signature"` | HTML5 Canvas drawing pad. Outputs PNG data URL. |
|
|
263
312
|
| `"rich-text"` | Tall textarea for formatted text input. |
|
|
313
|
+
| `"markdown"` | Markdown source, rendered with the same engine as the docs drawer (headings, tables, lists, code blocks, callouts). Editable fields get a Write/Preview toggle; with `readOnly: true` the field is a pure content block — see below. |
|
|
264
314
|
|
|
265
315
|
```json
|
|
266
316
|
{
|
|
@@ -286,6 +336,64 @@ For rich inputs beyond standard HTML types:
|
|
|
286
336
|
}
|
|
287
337
|
```
|
|
288
338
|
|
|
339
|
+
#### Markdown content blocks
|
|
340
|
+
|
|
341
|
+
`readOnly: true` + `x-lt-widget: "markdown"` turns a field into a rendered content
|
|
342
|
+
block: the markdown in its `default` displays as HTML inside the form — headings,
|
|
343
|
+
tables, checklists, callouts. The versioned schema carries the page source itself,
|
|
344
|
+
so review instructions and SOPs version with the form they belong to, and the
|
|
345
|
+
source rides along in the resolver payload like any read-only field.
|
|
346
|
+
|
|
347
|
+
```json
|
|
348
|
+
{
|
|
349
|
+
"properties": {
|
|
350
|
+
"review_guide": {
|
|
351
|
+
"type": "string",
|
|
352
|
+
"readOnly": true,
|
|
353
|
+
"x-lt-widget": "markdown",
|
|
354
|
+
"x-lt-span": 2,
|
|
355
|
+
"default": "### Review checklist\n\n1. Confirm the **legal name** matches.\n2. Send a test message before approving.\n\n> Escalate non-standard contract language to legal."
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Without `readOnly`, the field is a markdown *editor* — the resolver writes source in
|
|
362
|
+
a Write/Preview toggle and the submitted value is the markdown text.
|
|
363
|
+
|
|
364
|
+
### Payload Binding (`x-lt-bind`)
|
|
365
|
+
|
|
366
|
+
The form is flat; the payload the workflow consumes rarely is. A field may declare
|
|
367
|
+
`x-lt-bind` — the path its value occupies in the resolver payload (dot keys, optional
|
|
368
|
+
`[n]` indices). The dashboard maps the flat form through the binds on submit, and
|
|
369
|
+
reverse-maps workflow-seeded `envelope.formDefaults` through them to prefill. A field
|
|
370
|
+
with no bind lands at its own name at the payload root (1:1).
|
|
371
|
+
|
|
372
|
+
```json
|
|
373
|
+
{
|
|
374
|
+
"properties": {
|
|
375
|
+
"customer_name": { "type": "string", "x-lt-bind": "customer.name" },
|
|
376
|
+
"contact_email": { "type": "string", "format": "email", "x-lt-bind": "customer.email" },
|
|
377
|
+
"tier": { "type": "string", "enum": ["starter", "professional"], "x-lt-bind": "contract.tier" },
|
|
378
|
+
"notes": { "type": "string", "format": "textarea" }
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Submitting `{ customer_name, contact_email, tier, notes }` stores:
|
|
384
|
+
|
|
385
|
+
```json
|
|
386
|
+
{
|
|
387
|
+
"customer": { "name": "…", "email": "…" },
|
|
388
|
+
"contract": { "tier": "…" },
|
|
389
|
+
"notes": "…"
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Only the FORM is versioned on the role — the payload shape is the workflow's own
|
|
394
|
+
contract, produced by the binds. Evolve the form and its binds together, and the
|
|
395
|
+
workflow's resolver type in the same commit.
|
|
396
|
+
|
|
289
397
|
### Layout Options (`x-lt-layout`)
|
|
290
398
|
|
|
291
399
|
Control how fields are arranged:
|
package/docs/maintenance.md
CHANGED
|
@@ -28,6 +28,7 @@ await start({
|
|
|
28
28
|
{ target: 'jobs', olderThan: '14 days', action: 'delete', hasEntity: false },
|
|
29
29
|
{ target: 'jobs', olderThan: '14 days', action: 'prune', hasEntity: true },
|
|
30
30
|
{ target: 'jobs', olderThan: '180 days', action: 'delete', pruned: true },
|
|
31
|
+
{ target: 'escalations', olderThan: '180 days', action: 'delete' },
|
|
31
32
|
],
|
|
32
33
|
},
|
|
33
34
|
});
|
|
@@ -42,14 +43,15 @@ await start({
|
|
|
42
43
|
|
|
43
44
|
## What gets cleaned
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
Five categories of data are subject to maintenance:
|
|
46
47
|
|
|
47
48
|
| Category | Description |
|
|
48
49
|
|---|---|
|
|
49
|
-
| **Streams** |
|
|
50
|
+
| **Streams** | Stream messages used internally by HotMesh to coordinate activities. These are pure infrastructure; no user-facing data resides here. |
|
|
50
51
|
| **Transient jobs** | Job rows where the `entity` column is `NULL`. These represent activity executions, signal deliveries, and other bookkeeping that is not tied to a named workflow entity. They serve no purpose after execution completes. |
|
|
51
52
|
| **Entity jobs** | Job rows where `entity` is set. These represent actual workflow instances -- the rows that back `Durable.Client.workflow.search()` calls and execution exports. Deleting them removes the workflow record entirely. |
|
|
52
53
|
| **Pruned jobs** | Entity jobs that have already had their execution artifacts stripped (the `pruned_at` column is not `NULL`). These retain core data but no longer carry execution scaffolding. They can be hard-deleted after a longer retention window. |
|
|
54
|
+
| **Terminal escalations** | Escalation rows in a terminal status (`resolved`, `cancelled`, `expired`). Terminal rows are inert audit records -- every engine state transition guards on `status = 'pending'`, so aging them out is safe for live waiters, claims, and signal delivery. The engine-owned prune deletes in atomic batches until the retention horizon is drained. Pending rows are never touched. |
|
|
53
55
|
|
|
54
56
|
## Prune vs. delete
|
|
55
57
|
|
|
@@ -93,11 +95,12 @@ Defined in `types/maintenance.ts`. Each rule describes a single cleanup operatio
|
|
|
93
95
|
|
|
94
96
|
| Field | Type | Required | Description |
|
|
95
97
|
|---|---|---|---|
|
|
96
|
-
| `target` | `'streams' \| 'jobs'` | Yes | The resource type to act on. |
|
|
98
|
+
| `target` | `'streams' \| 'jobs' \| 'escalations'` | Yes | The resource type to act on. |
|
|
97
99
|
| `action` | `'delete' \| 'prune'` | Yes | Whether to remove entirely or strip execution artifacts. |
|
|
98
100
|
| `olderThan` | `string` | Yes | A PostgreSQL interval expression: `'7 days'`, `'24 hours'`, `'90 days'`. |
|
|
99
101
|
| `hasEntity` | `boolean` | No | When `target` is `'jobs'`: `true` selects entity jobs, `false` selects transient jobs (where `entity IS NULL`). |
|
|
100
102
|
| `pruned` | `boolean` | No | When `true`, only targets jobs where `pruned_at IS NOT NULL` -- jobs that have already been pruned. |
|
|
103
|
+
| `statuses` | `Array<'resolved' \| 'cancelled' \| 'expired'>` | No | When `target` is `'escalations'`: which terminal statuses to age out. Defaults to all three. |
|
|
101
104
|
|
|
102
105
|
### `LTMaintenanceConfig`
|
|
103
106
|
|
|
@@ -143,7 +143,7 @@ if (ctx) {
|
|
|
143
143
|
}
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
The MCP client (`callServerTool`) also reads `ToolContext` automatically. When no explicit `authContext` is passed, it derives `_auth` from the ambient context
|
|
146
|
+
The MCP client (`callServerTool`) also reads `ToolContext` automatically. When no explicit `authContext` is passed, it derives `_auth` from the ambient context — YAML workflow workers get auth propagation this way without any explicit wiring.
|
|
147
147
|
|
|
148
148
|
## Delegation Tokens
|
|
149
149
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotmeshio/long-tail",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "Long Tail Workflows — Durable AI workflows with human-in-the-loop escalation. Powered by PostgreSQL.",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"@anthropic-ai/sdk": "^0.92.0",
|
|
73
73
|
"@aws-sdk/client-s3": "^3.1017.0",
|
|
74
74
|
"@aws-sdk/s3-request-presigner": "^3.1045.0",
|
|
75
|
-
"@hotmeshio/hotmesh": "^0.
|
|
75
|
+
"@hotmeshio/hotmesh": "^0.26.1",
|
|
76
76
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
77
77
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.215.0",
|
|
78
78
|
"@opentelemetry/resources": "^2.5.1",
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import{a as l,j as e}from"./vendor-query-B2UbickB.js";import{b as S,f as w,j as _,c as E}from"./roles-lHLgLwOY.js";import{J as C}from"./index-MbEPoyTe.js";import{g as j,B as O,V as R,ah as q}from"./vendor-icons-DOIDKgo6.js";import{e as V}from"./vendor-react-CXumBFUA.js";const J=`{
|
|
2
|
-
"type": "object",
|
|
3
|
-
"properties": {
|
|
4
|
-
"approved": { "type": "boolean", "title": "Approve?" },
|
|
5
|
-
"notes": { "type": "string", "title": "Notes" }
|
|
6
|
-
},
|
|
7
|
-
"required": ["approved"]
|
|
8
|
-
}`;function v(o){const t=o.trim();if(!t)return{ok:!0,value:null};try{const r=JSON.parse(t);return typeof r!="object"||Array.isArray(r)||r===null?{ok:!1}:{ok:!0,value:r}}catch{return{ok:!1}}}function F(){const{role:o}=V(),{data:t,isLoading:r}=S(),n=w(),s=((t==null?void 0:t.roles)??[]).find(a=>a.role===o),[c,i]=l.useState(""),[h,u]=l.useState(""),[N,d]=l.useState(!1),[g,x]=l.useState(!1),[p,m]=l.useState(!1),f=l.useRef(null);l.useEffect(()=>{!s||f.current===s.role||(f.current=s.role,i(s.form_schema?JSON.stringify(s.form_schema,null,2):""),u(""),d(!1),m(!1))},[s]);const y=a=>{i(a),d(!0),x(!1),m(!v(a).ok)},b=()=>{const a=v(c);if(!a.ok||!s){m(!a.ok);return}n.mutate({role:s.role,form_schema:a.value??null,change_summary:h.trim()||void 0},{onSuccess:()=>{d(!1),x(!0),u(""),window.setTimeout(()=>x(!1),2500)}})},k=a=>{i(a?JSON.stringify(a,null,2):""),d(!0),x(!1),m(!1)};return r?e.jsxs("div",{className:"animate-pulse space-y-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-64"}),e.jsx("div",{className:"h-96 bg-surface-sunken rounded"})]}):s?e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-start justify-between mb-10",children:[e.jsx("div",{children:e.jsxs("div",{className:"flex items-center gap-3 mb-1",children:[e.jsx(j,{className:"w-5 h-5 text-accent",strokeWidth:1.5}),e.jsx("h1",{className:"text-lg font-medium text-text-primary",children:"Escalation Schema"}),e.jsx("button",{onClick:()=>{window.location.hash="#docs:dashboard.md:escalation-schema"},className:"text-text-quaternary hover:text-accent transition-colors",title:"Open docs for this page",children:e.jsx(O,{className:"w-4 h-4",strokeWidth:1.5})}),e.jsx("span",{className:"font-mono text-sm text-text-tertiary",children:s.role}),s.current_schema_version!=null&&e.jsxs("span",{className:"text-[10px] font-mono text-text-quaternary",children:["v",s.current_schema_version," in use"]})]})}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[g&&e.jsxs("span",{className:"flex items-center gap-1 text-xs text-status-success animate-page-enter",children:[e.jsx(R,{className:"w-3 h-3"})," Saved"]}),n.error&&e.jsx("span",{className:"text-xs text-status-error max-w-[220px] truncate",children:n.error.message}),e.jsx("button",{onClick:b,disabled:!N||p||n.isPending,className:"px-3 py-1.5 text-xs rounded-md bg-accent text-text-inverse hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:n.isPending?"Saving…":"Save Version"})]})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-16 items-start",children:[e.jsxs("div",{className:"col-span-2 space-y-6",children:[e.jsx("textarea",{value:c,onChange:a=>y(a.target.value),rows:28,spellCheck:!1,className:"input text-xs font-mono w-full resize-y leading-relaxed",placeholder:J}),p&&e.jsx("p",{className:"text-[10px] text-status-error -mt-4",children:"Invalid JSON"}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1.5",children:"Change Summary"}),e.jsx("input",{type:"text",value:h,onChange:a=>u(a.target.value),placeholder:"e.g., Added lotNumber field for shoe orders",className:"input text-sm w-full"}),e.jsx("p",{className:"text-[10px] text-text-quaternary mt-1.5",children:"Recorded on the version this save creates."})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2 mb-3 pb-2 border-b border-surface-border",children:[e.jsx(q,{className:"w-4 h-4 text-text-quaternary",strokeWidth:1.5}),e.jsx("h2",{className:"section-h2",children:"Versions"})]}),e.jsx(L,{role:s.role,onLoad:k})]})]})]}):e.jsxs("div",{className:"flex flex-col items-center justify-center py-20 text-center",children:[e.jsx(j,{className:"w-12 h-12 text-text-quaternary mb-4",strokeWidth:1}),e.jsx("h2",{className:"text-lg font-medium text-text-primary mb-2",children:"Role not found"})]})}function L({role:o,onLoad:t}){const{data:r}=_(o),n=(r==null?void 0:r.versions)??[];return n.length===0?e.jsx("p",{className:"text-[11px] text-text-tertiary leading-relaxed",children:"Versions appear here after the first save. Each schema change adds one; earlier versions stay viewable and loadable as an editing base."}):e.jsx("div",{className:"divide-y divide-surface-border/40",children:n.map(s=>e.jsx(P,{role:o,summary:s,onLoad:t},s.version))})}function P({role:o,summary:t,onLoad:r}){const[n,s]=l.useState(!1),c=E(o,t.version,n);return e.jsxs("div",{className:"py-2",children:[e.jsxs("button",{onClick:()=>s(i=>!i),className:"w-full text-left group",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("span",{className:"text-xs font-mono text-text-secondary",children:["v",t.version]}),t.is_current&&e.jsx("span",{className:"text-[9px] font-bold uppercase tracking-wider text-accent",children:"current"}),e.jsx("span",{className:"flex-1"}),e.jsx("span",{className:"text-[10px] text-text-quaternary shrink-0",children:new Date(t.created_at).toLocaleDateString()})]}),t.change_summary&&e.jsx("p",{className:"mt-0.5 text-[10px] text-text-tertiary truncate",children:t.change_summary})]}),n&&e.jsx("div",{className:"mt-2 space-y-2",children:c.data?e.jsxs(e.Fragment,{children:[c.data.form_schema?e.jsx(C,{data:c.data.form_schema,defaultCollapsed:!0}):e.jsx("p",{className:"text-[10px] text-text-quaternary",children:"This version carries no form schema."}),e.jsx("button",{onClick:()=>r(c.data.form_schema??null),className:"text-[10px] text-accent hover:underline",children:"Load into editor"})]}):e.jsx("p",{className:"text-[10px] text-text-quaternary",children:"Loading…"})})]})}export{F as EscalationSchemaPage};
|
|
9
|
-
//# sourceMappingURL=EscalationSchemaPage-hzFtuyha.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"EscalationSchemaPage-hzFtuyha.js","sources":["../../src/pages/admin/roles/EscalationSchemaPage.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Braces, Check, History, BookOpen } from 'lucide-react';\nimport {\n useRoleDetails,\n useRoleSchema,\n useRoleSchemaVersions,\n useUpdateRole,\n type RoleSchemaVersionSummary,\n} from '../../../api/roles';\nimport { JsonViewer } from '../../../components/common/data/JsonViewer';\n\n/**\n * The escalation form schema, on its own page (/admin/roles/:role/schema).\n * This is the form a person completes to resolve the role's escalations —\n * versioned, pinnable by workflows (conditionLT schemaVersion), and worth a\n * full editing surface. Saving here writes ONLY the schema: one PATCH with\n * form_schema (+ optional change summary); every actual change appends the\n * next immutable version.\n */\n\nconst PLACEHOLDER = `{\n \"type\": \"object\",\n \"properties\": {\n \"approved\": { \"type\": \"boolean\", \"title\": \"Approve?\" },\n \"notes\": { \"type\": \"string\", \"title\": \"Notes\" }\n },\n \"required\": [\"approved\"]\n}`;\n\nfunction safeParseObject(text: string): { ok: boolean; value?: Record<string, unknown> | null } {\n const trimmed = text.trim();\n if (!trimmed) return { ok: true, value: null };\n try {\n const parsed = JSON.parse(trimmed);\n if (typeof parsed !== 'object' || Array.isArray(parsed) || parsed === null) return { ok: false };\n return { ok: true, value: parsed };\n } catch {\n return { ok: false };\n }\n}\n\nexport function EscalationSchemaPage() {\n const { role: roleKey } = useParams<{ role: string }>();\n const { data, isLoading } = useRoleDetails();\n const updateRole = useUpdateRole();\n\n const role = (data?.roles ?? []).find((r) => r.role === roleKey);\n\n const [text, setText] = useState('');\n const [summary, setSummary] = useState('');\n const [dirty, setDirty] = useState(false);\n const [savedOk, setSavedOk] = useState(false);\n const [jsonError, setJsonError] = useState(false);\n\n // Seed the editor from the live schema exactly once per role — refetches\n // (cache invalidations, saves) must not clobber in-progress edits.\n const seededFor = useRef<string | null>(null);\n useEffect(() => {\n if (!role || seededFor.current === role.role) return;\n seededFor.current = role.role;\n setText(role.form_schema ? JSON.stringify(role.form_schema, null, 2) : '');\n setSummary('');\n setDirty(false);\n setJsonError(false);\n }, [role]);\n\n const handleEdit = (value: string) => {\n setText(value);\n setDirty(true);\n setSavedOk(false);\n setJsonError(!safeParseObject(value).ok);\n };\n\n const handleSave = () => {\n const parsed = safeParseObject(text);\n if (!parsed.ok || !role) { setJsonError(!parsed.ok); return; }\n // Saving just the schema — nothing else rides this PATCH.\n updateRole.mutate(\n { role: role.role, form_schema: parsed.value ?? null, change_summary: summary.trim() || undefined },\n {\n onSuccess: () => {\n setDirty(false);\n setSavedOk(true);\n setSummary('');\n window.setTimeout(() => setSavedOk(false), 2500);\n },\n },\n );\n };\n\n const loadIntoEditor = (schema: Record<string, unknown> | null) => {\n setText(schema ? JSON.stringify(schema, null, 2) : '');\n setDirty(true);\n setSavedOk(false);\n setJsonError(false);\n };\n\n if (isLoading) {\n return (\n <div className=\"animate-pulse space-y-4\">\n <div className=\"h-8 bg-surface-sunken rounded w-64\" />\n <div className=\"h-96 bg-surface-sunken rounded\" />\n </div>\n );\n }\n\n if (!role) {\n return (\n <div className=\"flex flex-col items-center justify-center py-20 text-center\">\n <Braces className=\"w-12 h-12 text-text-quaternary mb-4\" strokeWidth={1} />\n <h2 className=\"text-lg font-medium text-text-primary mb-2\">Role not found</h2>\n </div>\n );\n }\n\n return (\n <div>\n {/* ── Header ── */}\n <div className=\"flex items-start justify-between mb-10\">\n <div>\n <div className=\"flex items-center gap-3 mb-1\">\n <Braces className=\"w-5 h-5 text-accent\" strokeWidth={1.5} />\n <h1 className=\"text-lg font-medium text-text-primary\">Escalation Schema</h1>\n <button\n onClick={() => { window.location.hash = '#docs:dashboard.md:escalation-schema'; }}\n className=\"text-text-quaternary hover:text-accent transition-colors\"\n title=\"Open docs for this page\"\n >\n <BookOpen className=\"w-4 h-4\" strokeWidth={1.5} />\n </button>\n <span className=\"font-mono text-sm text-text-tertiary\">{role.role}</span>\n {role.current_schema_version != null && (\n <span className=\"text-[10px] font-mono text-text-quaternary\">\n v{role.current_schema_version} in use\n </span>\n )}\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 shrink-0\">\n {savedOk && (\n <span className=\"flex items-center gap-1 text-xs text-status-success animate-page-enter\">\n <Check className=\"w-3 h-3\" /> Saved\n </span>\n )}\n {updateRole.error && (\n <span className=\"text-xs text-status-error max-w-[220px] truncate\">\n {(updateRole.error as Error).message}\n </span>\n )}\n <button\n onClick={handleSave}\n disabled={!dirty || jsonError || updateRole.isPending}\n className=\"px-3 py-1.5 text-xs rounded-md bg-accent text-text-inverse hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {updateRole.isPending ? 'Saving…' : 'Save Version'}\n </button>\n </div>\n </div>\n\n {/* ── Editor + version rail ── */}\n <div className=\"grid grid-cols-3 gap-16 items-start\">\n <div className=\"col-span-2 space-y-6\">\n <textarea\n value={text}\n onChange={(e) => handleEdit(e.target.value)}\n rows={28}\n spellCheck={false}\n className=\"input text-xs font-mono w-full resize-y leading-relaxed\"\n placeholder={PLACEHOLDER}\n />\n {jsonError && <p className=\"text-[10px] text-status-error -mt-4\">Invalid JSON</p>}\n\n <div>\n <label className=\"block text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1.5\">\n Change Summary\n </label>\n <input\n type=\"text\"\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n placeholder=\"e.g., Added lotNumber field for shoe orders\"\n className=\"input text-sm w-full\"\n />\n <p className=\"text-[10px] text-text-quaternary mt-1.5\">\n Recorded on the version this save creates.\n </p>\n </div>\n </div>\n\n {/* Version history — view any snapshot, load one as the editing base */}\n <div>\n <div className=\"flex items-center gap-2 mb-3 pb-2 border-b border-surface-border\">\n <History className=\"w-4 h-4 text-text-quaternary\" strokeWidth={1.5} />\n <h2 className=\"section-h2\">Versions</h2>\n </div>\n <VersionRail role={role.role} onLoad={loadIntoEditor} />\n </div>\n </div>\n </div>\n );\n}\n\nfunction VersionRail({\n role,\n onLoad,\n}: {\n role: string;\n onLoad: (schema: Record<string, unknown> | null) => void;\n}) {\n const { data } = useRoleSchemaVersions(role);\n const versions = data?.versions ?? [];\n\n if (versions.length === 0) {\n return (\n <p className=\"text-[11px] text-text-tertiary leading-relaxed\">\n Versions appear here after the first save. Each schema change adds one;\n earlier versions stay viewable and loadable as an editing base.\n </p>\n );\n }\n\n return (\n <div className=\"divide-y divide-surface-border/40\">\n {versions.map((v) => (\n <VersionEntry key={v.version} role={role} summary={v} onLoad={onLoad} />\n ))}\n </div>\n );\n}\n\nfunction VersionEntry({\n role,\n summary,\n onLoad,\n}: {\n role: string;\n summary: RoleSchemaVersionSummary;\n onLoad: (schema: Record<string, unknown> | null) => void;\n}) {\n const [open, setOpen] = useState(false);\n // Snapshot fetch is lazy — only when the entry is expanded.\n const snapshot = useRoleSchema(role, summary.version, open);\n\n return (\n <div className=\"py-2\">\n <button onClick={() => setOpen((o) => !o)} className=\"w-full text-left group\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-mono text-text-secondary\">v{summary.version}</span>\n {summary.is_current && (\n <span className=\"text-[9px] font-bold uppercase tracking-wider text-accent\">current</span>\n )}\n <span className=\"flex-1\" />\n <span className=\"text-[10px] text-text-quaternary shrink-0\">\n {new Date(summary.created_at).toLocaleDateString()}\n </span>\n </div>\n {summary.change_summary && (\n <p className=\"mt-0.5 text-[10px] text-text-tertiary truncate\">{summary.change_summary}</p>\n )}\n </button>\n {open && (\n <div className=\"mt-2 space-y-2\">\n {snapshot.data ? (\n <>\n {snapshot.data.form_schema\n ? <JsonViewer data={snapshot.data.form_schema} defaultCollapsed />\n : <p className=\"text-[10px] text-text-quaternary\">This version carries no form schema.</p>}\n <button\n onClick={() => onLoad((snapshot.data!.form_schema ?? null) as Record<string, unknown> | null)}\n className=\"text-[10px] text-accent hover:underline\"\n >\n Load into editor\n </button>\n </>\n ) : (\n <p className=\"text-[10px] text-text-quaternary\">Loading…</p>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"names":["PLACEHOLDER","safeParseObject","text","trimmed","parsed","EscalationSchemaPage","roleKey","useParams","data","isLoading","useRoleDetails","updateRole","useUpdateRole","role","r","setText","useState","summary","setSummary","dirty","setDirty","savedOk","setSavedOk","jsonError","setJsonError","seededFor","useRef","useEffect","handleEdit","value","handleSave","loadIntoEditor","schema","jsxs","jsx","Braces","BookOpen","Check","e","History","VersionRail","onLoad","useRoleSchemaVersions","versions","v","VersionEntry","open","setOpen","snapshot","useRoleSchema","o","Fragment","JsonViewer"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{a as w,j as e}from"./vendor-query-B2UbickB.js";import{b as Pe}from"./roles-lHLgLwOY.js";import{O as ye}from"./index-MbEPoyTe.js";import{i as Se}from"./useEventHooks-CeOIM1Ix.js";import{P as Ce}from"./PageHeader-DJo7yMUR.js";import{X as Me,ae as ie,av as Le,ar as qe}from"./vendor-icons-DOIDKgo6.js";import{L as Q,f as Fe,c as Oe}from"./vendor-react-CXumBFUA.js";const ge=800,je=270,I=40,Ae=76,ce=24,Ee=72,oe=ge-I-Ae,be=je-ce-Ee,$=ce+be,he=I+oe,j="0.5s cubic-bezier(0.4, 0, 0.2, 1)",me=9;function We(t){return`M ${(t.x-me).toFixed(1)} ${t.y.toFixed(1)} L ${(t.x+me).toFixed(1)} ${t.y.toFixed(1)}`}function T(t){if(t.length===0)return"";if(t.length===1)return We(t[0]);const n=[];for(let l=0;l<t.length-1;l++){const i=t[Math.max(0,l-1)],c=t[l],p=t[l+1],a=t[Math.min(t.length-1,l+2)],d=(c.x+(p.x-i.x)/6).toFixed(1),u=(c.y+(p.y-i.y)/6).toFixed(1),o=(p.x-(a.x-c.x)/6).toFixed(1),x=(p.y-(a.y-c.y)/6).toFixed(1);l===0&&n.push(`M ${c.x.toFixed(1)} ${c.y.toFixed(1)}`),n.push(`C ${d} ${u}, ${o} ${x}, ${p.x.toFixed(1)} ${p.y.toFixed(1)}`)}return n.join(" ")}function Ie(t,n){if(t.length<2||n.length<2)return"";const l=T(t),i=[...n].reverse(),c=T(i),p=c.slice(c.indexOf("C"));return`${l} L ${i[0].x.toFixed(1)} ${i[0].y.toFixed(1)} ${p} Z`}function Te(t){return t>=1e4?`${Math.round(t/1e3)}k`:t>=1e3?`${(t/1e3).toFixed(1)}k`:String(t)}function ne(t){return t.length>0?t[t.length-1]:void 0}const O="#6366f1",D="#0ea5e9",q="#1E3A8A",X="#059669",ve="#b0e0e6",A="#5b93ad",De=13;function Be(t,n){const l=[...t].sort((a,d)=>a.y-d.y);let i=-1/0;const c=l.map(a=>{const d=Math.max(a.y+3,i+De);return i=d,{...a,labelY:d}}),p=c.length>0?c[c.length-1].labelY-n:0;if(p>0)for(const a of c)a.labelY-=p;return c}function ze({stations:t,selectedRole:n,onSelect:l,onUpstreamSelect:i,onPrioritySelect:c,periodHours:p}){const[a,d]=w.useState(null),u=t.length;if(u===0)return null;const o=s=>u===1?I+oe/2:I+s/(u-1)*oe,x=new Map(t.map((s,h)=>[s.role,o(h)])),y=t.map((s,h)=>{var P,V,G,U;const f=s.target_per_hour??null,C=f!=null?f*p:null;return{idx:h,x:o(h),expected:C,actual:((P=s.metric)==null?void 0:P.resolved)??0,active:((V=s.metric)==null?void 0:V.claimed)??0,pending:((G=s.metric)==null?void 0:G.pending)??0,priorityCount:((U=s.metric)==null?void 0:U.priority_count)??0}}),b=y.some(s=>s.expected!=null),v=Math.max(1,...y.map(s=>s.expected??0),...y.map(s=>s.actual),...y.map(s=>s.expected!=null?s.pending:0))*1.15,S=s=>ce+be*(1-Math.max(0,Math.min(s,v))/v),N=y.filter(s=>s.expected!=null),M=N.map(s=>({...s,y:S(s.expected)})),_=N.map(s=>({...s,y:S(s.actual)})),R=N.map(s=>({...s,y:S(s.active)})),k=N.map(s=>({...s,y:S(s.pending)})),B=T(M),z=T(_),H=T(R),Z=T(k),F=ne(_),K=ne(M),E=ne(R),J=ne(k),ee=_.length>=2&&F?`${z} L ${F.x.toFixed(1)} ${$} L ${_[0].x.toFixed(1)} ${$} Z`:"",W=N.some(s=>s.pending>0||s.active>0),te=W&&R.length>=2&&E?`${H} L ${E.x.toFixed(1)} ${$} L ${R[0].x.toFixed(1)} ${$} Z`:"",Y=W?Ie(k,R):"",r=N.some(s=>s.active>0),m=N.some(s=>s.pending>s.active),L=Be([K?{key:"target",text:"Target",color:X,y:K.y}:null,F?{key:"actual",text:"Resolved",color:q,y:F.y}:null,r&&E?{key:"active",text:"Claimed",color:O,y:E.y}:null,m&&J?{key:"queued",text:"Pending",color:D,y:J.y}:null].filter(Boolean),$+4);return e.jsxs("svg",{viewBox:`0 0 ${ge} ${je}`,className:"w-full h-full",preserveAspectRatio:"xMidYMid meet",style:{display:"block"},children:[t.map(s=>{if(!s.parent_role)return null;const h=x.get(s.parent_role);if(h==null)return null;const f=o(t.findIndex(C=>C.role===s.role));return e.jsx("line",{x1:h,y1:$,x2:f,y2:$,stroke:"#e2e8f0",strokeWidth:.75},`dep-${s.role}`)}),ee&&e.jsx("path",{d:ee,fill:q,opacity:.05,style:{transition:`d ${j}`}}),te&&e.jsx("path",{d:te,fill:O,opacity:.12,style:{transition:`d ${j}`}}),Y&&e.jsx("path",{d:Y,fill:D,opacity:.12,style:{transition:`d ${j}`}}),B&&e.jsx("path",{d:B,fill:"none",stroke:X,strokeWidth:.5,strokeLinejoin:"round",opacity:.9,style:{transition:`d ${j}`}}),M.map(s=>e.jsx("text",{x:s.x,y:s.y-3.5,textAnchor:"middle",fontSize:6.5,fill:X,fontFamily:"ui-monospace, monospace",fontWeight:"500",opacity:.7,style:{transition:`y ${j}`},children:Math.round(s.expected)},`tval-${s.idx}`)),W&&Z&&e.jsx("path",{d:Z,fill:"none",stroke:D,strokeWidth:.75,strokeDasharray:"2 3",strokeLinecap:"round",opacity:.55,style:{transition:`d ${j}`}}),H&&e.jsx("path",{d:H,fill:"none",stroke:O,strokeWidth:.75,strokeDasharray:"2 3",strokeLinecap:"round",opacity:.5,style:{transition:`d ${j}`}}),z&&e.jsx("path",{d:z,fill:"none",stroke:q,strokeWidth:.6,strokeLinejoin:"round",strokeLinecap:"round",opacity:.65,style:{transition:`d ${j}`}}),R.map((s,h)=>{if(s.pending<=0&&s.active<=0)return null;const f=Math.max(2.5,Math.min(5,2.5+s.active/10)),C=s.pending>s.active,P=Math.min(k[h].y-4,s.y-f-4);return e.jsxs("g",{children:[s.pending>0&&e.jsx("text",{x:s.x,y:P,textAnchor:"middle",fontSize:7.5,fill:C?D:"#64748b",fontFamily:"ui-monospace, monospace",fontWeight:"600",style:{transition:`y ${j}`},children:s.pending}),e.jsxs("g",{transform:`translate(${s.x} ${s.y})`,style:{transition:`transform ${j}`},children:[e.jsx("circle",{r:f,fill:O,opacity:.85}),s.active>0&&e.jsx("text",{y:f+9,textAnchor:"middle",fontSize:7.5,fill:O,fontFamily:"ui-monospace, monospace",fontWeight:"600",children:s.active})]})]},`active-${s.idx}`)}),y.map(s=>{var ue;const h=t[s.idx],f=s.x,C=s.expected!=null?S(s.actual):$,P=Math.max(2.25,Math.min(4,2.25+s.actual/160)),V=h.role===n,G=a===s.idx,U=s.expected==null?"idle · set a target rate to plot pace":`${s.actual} resolved · target ${Math.round(s.expected)}`+(s.pending>s.active?` · ${s.pending-s.active} pending`:"")+(s.active>0?` · ${s.active} claimed`:"")+(s.priorityCount>0?` · ${s.priorityCount} priority`:""),se=U.length*5.5+20,de=Math.max(I+se/2+4,Math.min(he-se/2-4,f)),pe=C-P-10;return e.jsxs("g",{onClick:()=>l(h.role),onMouseEnter:()=>d(s.idx),onMouseLeave:()=>d(null),style:{cursor:"pointer"},children:[e.jsxs("g",{transform:`translate(${f} ${C})`,style:{transition:`transform ${j}`},children:[e.jsx("circle",{r:22,fill:"transparent"}),e.jsx("circle",{r:P,fill:q,opacity:.9,style:{transition:`r ${j}`}}),s.expected!=null&&e.jsx("text",{y:-P-5,textAnchor:"middle",fontSize:9.5,fill:q,fontFamily:"ui-monospace, monospace",fontWeight:"500",children:Te(s.actual)}),G&&!V&&e.jsx("circle",{r:P+3,fill:"none",stroke:q,strokeWidth:1,opacity:.4}),V&&e.jsx("circle",{r:P+4,fill:"none",stroke:"#6366f1",strokeWidth:2})]}),s.priorityCount>0&&e.jsxs("g",{transform:`translate(${f+15} ${Math.max(C-15,12)})`,onClick:le=>{le.stopPropagation(),c==null||c(h.role)},style:{cursor:"pointer",transition:`transform ${j}`},children:[e.jsx("title",{children:`${s.priorityCount} past the priority threshold — click to pull oldest first`}),e.jsx("circle",{r:6.5+(String(s.priorityCount).length-1)*2,fill:ve,opacity:.9}),e.jsx("text",{y:2.5,textAnchor:"middle",fontSize:7,fill:A,fontFamily:"ui-monospace, monospace",fontWeight:"700",children:s.priorityCount})]}),(((ue=h.upstream_roles)==null?void 0:ue.length)??0)>0&&e.jsxs("g",{transform:`translate(${f-20} ${$})`,onClick:le=>{le.stopPropagation(),i==null||i(h.upstream_roles[0])},style:{cursor:"pointer"},children:[e.jsx("title",{children:`Fed by ${h.upstream_roles.join(", ")} — click to view that sequence`}),e.jsx("circle",{r:9,cy:-6,fill:"transparent"}),e.jsx("path",{d:"M -7 -14 C -3 -14 -1 -9 0 -2",fill:"none",stroke:"#94a3b8",strokeWidth:1,strokeDasharray:"2 2",strokeLinecap:"round"}),e.jsx("path",{d:"M 0 -2 L 8 -2",stroke:"#94a3b8",strokeWidth:1,strokeLinecap:"round"}),e.jsx("path",{d:"M 8 -2 l -3.5 -2 v 4 z",fill:"#94a3b8"})]}),e.jsx("text",{x:f,y:$+14,textAnchor:"middle",fontSize:9.5,fill:"#475569",fontFamily:"ui-sans-serif, sans-serif",fontWeight:"500",children:h.title??h.role}),e.jsx("text",{x:f,y:$+26,textAnchor:"middle",fontSize:8,fill:"#94a3b8",fontFamily:"ui-monospace, monospace",children:h.role}),G&&e.jsxs("g",{children:[e.jsx("rect",{x:de-se/2,y:pe-16,width:se,height:19,rx:3,fill:"#0f172a",opacity:.88}),e.jsx("text",{x:de,y:pe-3,textAnchor:"middle",fontSize:8.5,fill:"white",fontFamily:"ui-monospace, monospace",children:U})]})]},h.role)}),!b&&e.jsx("text",{x:I+oe/2,y:$+48,textAnchor:"middle",fontSize:8.5,fill:"#94a3b8",fontFamily:"ui-sans-serif, sans-serif",children:"Set target_per_hour on each role in Admin → Roles to enable the pace chart"}),L.map(s=>e.jsx("text",{x:he+6,y:s.labelY,fontSize:8,fill:s.color,fontFamily:"ui-sans-serif, sans-serif",fontWeight:s.key==="actual"?700:500,opacity:s.key==="active"?.85:1,style:{transition:`y ${j}`},children:s.text},s.key))]})}function xe(t){const n=`/escalations/available?role=${encodeURIComponent(t.role)}`;if(t.priority_facet){const l=JSON.stringify([{field:`metadata.${t.priority_facet}`,direction:"asc"}]);return`${n}&orderBy=${encodeURIComponent(l)}`}return`${n}&sort_by=created_at&order=asc`}const Ne=["15m","1h","24h","7d","30d"],He=t=>Ne.includes(t);function ae(t){if(t==null)return"—";if(t<1){const n=Math.round(t*60);return n<=0?"< 1s":`${n}s`}return t<60?`${t.toFixed(0)}m`:`${(t/60).toFixed(1)}h`}function re({label:t,value:n}){return e.jsxs("div",{className:"flex items-center justify-between py-1",children:[e.jsx("span",{className:"text-[10px] text-text-tertiary uppercase tracking-wider",children:t}),e.jsx("span",{className:"text-xs font-mono font-medium text-text-primary tabular-nums",children:n})]})}function Ye({period:t,onChange:n}){return e.jsx("div",{className:"flex items-center gap-1 mb-4",children:Ne.map(l=>e.jsx("button",{onClick:()=>n(l),className:`px-2 py-0.5 text-[10px] font-mono rounded transition-colors ${t===l?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:l},l))})}function Ve({role:t,globalPeriod:n,onClose:l}){const[i,c]=w.useState(He(n)?n:"24h"),{data:p}=ye(i),a=p==null?void 0:p.stations.find(x=>x.role===t.role),d=t.sla_minutes??void 0,u=t.target_per_hour??void 0,o=t.worker_count??void 0;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-start justify-between mb-1",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex items-baseline gap-1.5 flex-wrap",children:[e.jsx("span",{className:"text-sm font-mono font-bold text-text-primary",children:t.role}),t.title&&e.jsx("span",{className:"text-[10px] text-text-secondary",children:t.title})]}),t.description&&e.jsx("p",{className:"text-[10px] text-text-tertiary mt-0.5 line-clamp-2 leading-relaxed",children:t.description})]}),e.jsx("button",{onClick:l,className:"text-text-quaternary hover:text-text-secondary transition-colors mt-0.5 shrink-0 ml-2",children:e.jsx(Me,{className:"w-4 h-4"})})]}),e.jsxs(Q,{to:`/admin/roles/${encodeURIComponent(t.role)}`,className:"flex items-center gap-1 text-[10px] text-accent hover:underline mb-4",children:[e.jsx(ie,{className:"w-2.5 h-2.5"}),"Edit in Roles"]}),e.jsx(Ye,{period:i,onChange:c}),(u||d||o)&&e.jsxs("div",{className:"flex items-center gap-4 mb-4 text-[10px]",children:[u&&e.jsxs("div",{children:[e.jsxs("div",{className:"font-mono font-semibold text-text-primary",children:[u,"/h"]}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px]",children:"target"})]}),d&&e.jsxs("div",{children:[e.jsxs("div",{className:"font-mono font-semibold text-text-primary",children:[d,"m"]}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px]",children:"SLA"})]}),o&&e.jsxs("div",{children:[e.jsx("div",{className:"font-mono font-semibold text-text-primary",children:o}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px]",children:"workers"})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-x-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[9px] text-text-quaternary uppercase tracking-wider mb-1.5",children:"Wait (queue)"}),e.jsx(re,{label:"P99",value:ae((a==null?void 0:a.wait.p99)??null)}),e.jsx(re,{label:"avg",value:ae((a==null?void 0:a.wait.avg)??null)})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-[9px] text-text-quaternary uppercase tracking-wider mb-1.5",children:"Work (proc.)"}),e.jsx(re,{label:"P99",value:ae((a==null?void 0:a.work.p99)??null)}),e.jsx(re,{label:"avg",value:ae((a==null?void 0:a.work.avg)??null)})]})]}),e.jsxs("div",{className:"border-t border-surface-border/40 pt-2 mt-3 space-y-1.5",children:[d&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-tertiary",children:"SLA target"}),e.jsxs("span",{className:"text-xs font-mono text-text-secondary",children:[d,"m"]})]}),a&&a.priority_count>0&&e.jsxs(Q,{to:xe(t),className:"flex items-center justify-between group",children:[e.jsxs("span",{className:"text-[10px]",style:{color:A},children:[a.priority_count," priority — pull oldest first"]}),e.jsx(ie,{className:"w-3 h-3 transition-colors",style:{color:A}})]})]}),e.jsx("div",{className:"mt-4 border-t border-surface-border/40 pt-3",children:e.jsxs(Q,{to:`/escalations/available?role=${encodeURIComponent(t.role)}`,className:"flex items-center gap-1.5 text-[10px] text-accent hover:underline",children:["View full queue",e.jsx(ie,{className:"w-2.5 h-2.5"})]})})]})}function Ge({allMetrics:t,orderedRoles:n,period:l}){const i=t.reduce((o,x)=>o+x.pending,0),c=t.reduce((o,x)=>o+x.resolved,0),p=t.reduce((o,x)=>o+x.priority_count,0),a=new Map(t.map(o=>[o.role,o])),d=t.filter(o=>o.priority_count>0).length,u=t.filter(o=>o.pending>0).length;return e.jsxs(e.Fragment,{children:[e.jsxs("p",{className:"text-[9px] text-text-quaternary uppercase tracking-wider mb-3",children:["Pipeline · ",l]}),e.jsx("div",{className:"mb-4",children:d>0?e.jsxs("p",{className:"text-[11px]",style:{color:A},children:[d," station",d>1?"s":""," with priority items"]}):u>0?e.jsxs("p",{className:"text-[11px] text-status-warning",children:[u," station",u>1?"s":""," with backlog"]}):c>0?e.jsx("p",{className:"text-[11px] text-status-success",children:"Flowing — queue clear"}):e.jsx("p",{className:"text-[11px] text-text-quaternary",children:"Quiet — awaiting work"})}),e.jsxs("div",{className:"space-y-1.5 mb-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[9px] text-text-quaternary uppercase tracking-wider",children:"Pending"}),e.jsx("span",{className:`text-xs font-mono tabular-nums ${i>0?"text-text-primary font-semibold":"text-text-quaternary"}`,children:i})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("span",{className:"text-[9px] text-text-quaternary uppercase tracking-wider",children:["Resolved · ",l]}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-text-secondary",children:c})]}),p>0&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[9px] text-text-quaternary uppercase tracking-wider",children:"Priority"}),e.jsx("span",{className:"text-xs font-mono tabular-nums font-semibold",style:{color:A},children:p})]})]}),e.jsx("div",{className:"border-t border-surface-border/40 pt-2 mb-1",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider flex-1",children:"Station"}),e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"pend"}),e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"clmd"}),e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"res"})]})}),e.jsx("div",{className:"space-y-0.5",children:n.map(o=>{const x=a.get(o.role),y=(x==null?void 0:x.pending)??0,b=(x==null?void 0:x.claimed)??0,g=(x==null?void 0:x.resolved)??0,v=(x==null?void 0:x.priority_count)??0,S=o.title||o.role,N=v>0,M=y>0;return e.jsxs("div",{className:"flex items-center gap-2 py-0.5",children:[e.jsxs("span",{className:`text-[10px] font-mono flex-1 truncate ${M?"text-text-primary":"text-text-quaternary"}`,children:[S,N&&e.jsxs("span",{className:"ml-1 text-[8px] font-semibold",style:{color:A},children:[v," PRIORITY"]})]}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${M?"text-text-primary font-semibold":"text-text-quaternary"}`,children:y>0?y:"—"}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${b>0?"text-accent":"text-text-quaternary"}`,children:b>0?b:"—"}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${g>0?"text-text-secondary":"text-text-quaternary"}`,children:g>0?g:"—"})]},o.role)})}),e.jsx("p",{className:"text-[9px] text-text-quaternary mt-5",children:"Select a station for queue detail."})]})}function Ue({role:t,allMetrics:n,orderedRoles:l,globalPeriod:i,onClose:c}){return e.jsx("div",{className:"w-[280px] shrink-0 px-6 py-8 flex flex-col overflow-y-auto min-h-0",children:t?e.jsx(Ve,{role:t,globalPeriod:i,onClose:c}):e.jsx(Ge,{allMetrics:n,orderedRoles:l,period:i})})}const ke=`${X}14`,$e=`${D}14`,we=`${O}14`,_e=`${q}14`,Qe=["15m","1h","24h","7d","30d"],Xe={"15m":.25,"1h":1,"24h":24,"7d":168,"30d":720};function Re(t){const n=t.filter(a=>a.ops_visible),l=new Map(n.map(a=>[a.role,a])),i=new Set,p=n.filter(a=>!a.parent_role||!l.has(a.parent_role)).map(a=>{const d=[],u=[{role:a,depth:0}];for(;u.length>0;){const o=u.shift();i.has(o.role.role)||(i.add(o.role.role),d.push(o),n.filter(x=>x.parent_role===o.role.role&&!i.has(x.role)).forEach(x=>u.push({role:x,depth:o.depth+1})))}return{origin:a,stations:d}});return n.filter(a=>!i.has(a.role)).forEach(a=>p.push({origin:a,stations:[{role:a,depth:0}]})),p.sort((a,d)=>d.stations.length-a.stations.length||a.origin.role.localeCompare(d.origin.role))}function fe(t){if(t==null)return"—";if(t<1){const n=Math.round(t*60);return n<=0?"< 1s":`${n}s`}return t<60?`${t.toFixed(0)}m`:`${(t/60).toFixed(1)}h`}function Ze(t,n){if(!n||t===0)return{pct:null,color:"bg-surface-border"};const l=t/n,i=Math.round(l*100),c=l>1?"bg-status-warning":l<.2?"bg-status-draft":"bg-status-success";return{pct:i,color:c}}function Ke({role:t,metric:n,selected:l,onClick:i}){var b;const c=(n==null?void 0:n.pending)??0,p=(n==null?void 0:n.claimed)??0,a=(n==null?void 0:n.resolved)??0,d=(n==null?void 0:n.priority_count)??0,u=t.target_per_hour??null,{pct:o,color:x}=Ze(c,u),y=o!=null?Math.min(100,o):0;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{role:"button",tabIndex:0,onClick:i,onKeyDown:g=>{g.key==="Enter"&&i()},className:`grid items-center gap-4 pr-3 cursor-pointer transition-colors ${l?"border-l-2 !border-l-accent pl-2.5":"pl-3"}`,style:{gridTemplateColumns:"1fr 64px 56px 56px 60px 72px 72px 104px"},children:[e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0 py-1.5",children:[e.jsx("span",{className:"font-mono font-bold text-text-primary truncate text-[11px]",children:t.role}),t.title&&e.jsx("span",{className:"text-[10px] text-text-tertiary truncate",children:t.title}),(((b=t.upstream_roles)==null?void 0:b.length)??0)>0&&e.jsx("span",{title:`Fed by ${t.upstream_roles.join(", ")}`,className:"shrink-0 leading-none",children:e.jsx(qe,{className:"w-3 h-3 text-text-quaternary"})})]}),e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:ke},children:e.jsx("span",{className:`text-xs font-mono tabular-nums ${u!=null?"text-text-secondary":"text-text-quaternary"}`,children:u??"—"})}),e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:$e},children:e.jsx("span",{className:`text-xs font-mono tabular-nums ${c>0?"text-text-primary font-semibold":"text-text-quaternary"}`,children:c})}),e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:we},children:e.jsx("span",{className:`text-xs font-mono tabular-nums ${p>0?"text-accent font-semibold":"text-text-quaternary"}`,children:p})}),e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:_e},children:e.jsx("span",{className:"text-xs font-mono tabular-nums text-text-quaternary",children:a})}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-right py-1.5 text-text-secondary",children:fe((n==null?void 0:n.wait.p99)??null)}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-right py-1.5 text-text-secondary",children:fe((n==null?void 0:n.work.p99)??null)}),e.jsxs("div",{className:"flex items-center gap-2 py-1.5",children:[e.jsx("div",{className:"w-12 h-1.5 bg-surface-sunken rounded-full overflow-hidden shrink-0",children:e.jsx("div",{className:`h-full rounded-full ${x}`,style:{width:`${y}%`}})}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums ${o!=null&&o>100?"text-status-warning font-semibold":"text-text-quaternary"}`,children:o!=null?`${o}%`:"—"})]})]}),d>0&&e.jsxs("div",{className:"flex items-center gap-1.5 pb-2 pl-3.5",children:[e.jsx("span",{className:"w-2.5 h-2.5 rounded-full dot-ring shrink-0",style:{backgroundColor:ve}}),e.jsxs(Q,{to:xe(t),className:"text-[10px] hover:underline",style:{color:A},onClick:g=>g.stopPropagation(),children:[d," priority — pull oldest first →"]})]})]})}function Je(){const t=[{label:"ROLE"},{label:"TARGET/H",band:ke,hue:X},{label:"PENDING",band:$e,hue:D},{label:"CLAIMED",band:we,hue:O},{label:"RESOLVED",band:_e,hue:q},{label:"P99 WAIT"},{label:"P99 WORK"},{label:"TREND"}];return e.jsx("div",{className:"grid items-center gap-4 px-3 border-b border-surface-border mb-0.5",style:{gridTemplateColumns:"1fr 64px 56px 56px 60px 72px 72px 104px"},children:t.map((n,l)=>n.band?e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:n.band},children:e.jsx("span",{className:"text-[9px] font-semibold uppercase tracking-wider",style:{color:n.hue},children:n.label})},n.label):e.jsx("span",{className:`text-[9px] font-semibold uppercase tracking-wider text-text-quaternary py-1.5 ${l>0&&l<7?"text-right":""}`,children:n.label},n.label))})}function et(){var Y;const[t,n]=w.useState("1h"),[l,i]=w.useState(null),[c,p]=w.useState(!1);Se();const{data:a,isLoading:d,refetch:u}=Pe(),{data:o,isLoading:x,refetch:y}=ye(t),b=(a==null?void 0:a.roles)??[],g=(o==null?void 0:o.stations)??[],v=w.useMemo(()=>Re(b),[b]),[S,N]=Fe(),M=S.get("fragment"),_=v.find(r=>r.origin.role===M)??v[0]??null,R=w.useCallback(r=>{i(null),N(m=>{const L=new URLSearchParams(m);return L.set("fragment",r),L})},[N]),k=(_==null?void 0:_.stations)??[],B=w.useMemo(()=>new Set(k.map(({role:r})=>r.role)),[k]),z=w.useMemo(()=>g.filter(r=>B.has(r.role)),[g,B]),H=w.useMemo(()=>k.map(({role:r})=>({role:r.role,title:r.title,parent_role:r.parent_role,target_per_hour:r.target_per_hour??null,upstream_roles:r.upstream_roles??[],metric:g.find(m=>m.role===r.role)})),[k,g]),Z=w.useCallback(r=>{const m=v.find(L=>L.stations.some(({role:s})=>s.role===r));m&&R(m.origin.role)},[v,R]),F=Oe(),K=w.useCallback(r=>{const m=b.find(L=>L.role===r);m&&F(xe(m))},[b,F]),E=((Y=k.find(({role:r})=>r.role===l))==null?void 0:Y.role)??null,J=d||x,ee=async()=>{p(!0),await Promise.all([u(),y()]),p(!1)},W=r=>i(m=>m===r?null:r),te=e.jsx("div",{className:"flex items-center gap-0.5",children:Qe.map(r=>e.jsx("button",{onClick:()=>n(r),className:`px-2.5 py-1 text-[10px] font-mono rounded transition-colors ${t===r?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:r},r))});return e.jsxs("div",{className:"flex flex-col flex-1 min-h-0",children:[e.jsx(Ce,{title:"Pace Board",docsHash:"#docs:dashboard.md:pace-board",center:te,actions:e.jsx("button",{onClick:ee,disabled:c,className:"text-text-quaternary hover:text-text-secondary transition-colors disabled:opacity-40",children:e.jsx(Le,{className:`w-3.5 h-3.5 ${c?"animate-spin":""}`})})}),J?e.jsxs("div",{className:"animate-pulse space-y-6 mt-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-64"}),e.jsx("div",{className:"h-64 bg-surface-sunken rounded w-full"}),e.jsx("div",{className:"space-y-3",children:[1,2,3].map(r=>e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-full"},r))})]}):v.length===0?e.jsxs("div",{className:"mt-8",children:[e.jsx("p",{className:"text-sm text-text-secondary mb-2",children:"Stations appear here once roles are marked visible in Operations."}),e.jsxs("p",{className:"text-xs text-text-tertiary",children:["Go to"," ",e.jsx(Q,{to:"/admin/roles",className:"text-accent hover:underline",children:"Roles"})," ","and enable ",e.jsx("strong",{children:"Visible in Operations"})," on roles that represent pipeline stations."]})]}):e.jsxs("div",{className:"flex flex-col flex-1 min-h-0",children:[v.length>1&&e.jsx("div",{className:"flex items-center gap-0.5 px-4 pt-2",children:v.map(r=>{const m=r.origin.role===(_==null?void 0:_.origin.role);return e.jsxs("button",{onClick:()=>R(r.origin.role),className:`px-2.5 py-1 text-[10px] font-mono rounded transition-colors ${m?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:[r.origin.title??r.origin.role,e.jsx("span",{className:`ml-1.5 tabular-nums ${m?"text-accent/60":"text-text-quaternary/60"}`,children:r.stations.length})]},r.origin.role)})}),e.jsxs("div",{className:"flex-1 min-h-0 flex items-stretch overflow-hidden",children:[e.jsx("div",{className:"flex-1 min-w-0 min-h-0 flex flex-col justify-center overflow-hidden p-4",children:e.jsx(ze,{stations:H,selectedRole:l,onSelect:W,onUpstreamSelect:Z,onPrioritySelect:K,periodHours:Xe[t]})}),e.jsx("div",{className:"w-px bg-surface-border shrink-0 self-stretch"}),e.jsx(Ue,{role:E,allMetrics:z,orderedRoles:k.map(r=>r.role),globalPeriod:t,onClose:()=>i(null)})]}),e.jsxs("div",{className:"max-h-[45vh] flex-none flex flex-col border-t border-surface-border overflow-hidden",children:[e.jsx(Je,{}),e.jsx("div",{className:"flex-1 min-h-0 overflow-y-auto divide-y divide-surface-border/30",children:k.map(({role:r})=>e.jsx(Ke,{role:r,metric:g.find(m=>m.role===r.role),selected:l===r.role,onClick:()=>W(r.role)},r.role))})]})]})]})}const it=Object.freeze(Object.defineProperty({__proto__:null,OperationsPage:et,buildFragments:Re},Symbol.toStringTag,{value:"Module"}));export{it as O,ze as P,Re as b,xe as p};
|
|
2
|
-
//# sourceMappingURL=OperationsPage-Cz_0K8tj.js.map
|