@hotmeshio/long-tail 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/build/api/escalations/accumulate.d.ts +96 -0
- package/build/api/escalations/accumulate.js +516 -0
- package/build/api/escalations/index.d.ts +1 -0
- package/build/api/escalations/index.js +10 -1
- package/build/api/roles.d.ts +4 -5
- package/build/api/roles.js +53 -4
- package/build/api/scan-codes/execute.js +2 -0
- package/build/api/scan-codes/verbs.d.ts +15 -0
- package/build/api/scan-codes/verbs.js +65 -0
- package/build/index.js +60 -0
- package/build/lib/db/schemas/036_role_portals.sql +12 -0
- package/build/routes/escalations/accumulate.d.ts +7 -0
- package/build/routes/escalations/accumulate.js +150 -0
- package/build/routes/escalations/index.js +5 -0
- package/build/sdk/index.d.ts +25 -0
- package/build/sdk/index.js +8 -0
- package/build/services/escalation/accumulate.d.ts +81 -0
- package/build/services/escalation/accumulate.js +154 -0
- package/build/services/escalation/index.d.ts +1 -0
- package/build/services/escalation/index.js +1 -0
- package/build/services/mcp/playwright-server/schemas.d.ts +2 -2
- package/build/services/orchestrator/condition.d.ts +40 -3
- package/build/services/orchestrator/condition.js +42 -2
- package/build/services/role/index.js +1 -0
- package/build/services/role/seed.js +6 -1
- package/build/services/role/sql.d.ts +6 -3
- package/build/services/role/sql.js +7 -2
- package/build/services/role/types.d.ts +38 -10
- package/build/services/role/types.js +10 -1
- package/build/services/scan-code/validate.js +16 -0
- package/build/shared/form-validation/display-only-widgets.d.ts +16 -0
- package/build/shared/form-validation/display-only-widgets.js +25 -0
- package/build/shared/form-validation/index.d.ts +2 -0
- package/build/shared/form-validation/index.js +2 -0
- package/build/shared/form-validation/x-lt-bind.d.ts +2 -14
- package/build/shared/form-validation/x-lt-bind.js +12 -8
- package/build/shared/form-validation/x-lt-invoke.d.ts +38 -0
- package/build/shared/form-validation/x-lt-invoke.js +103 -0
- package/build/system/mcp-servers/admin/escalations.js +39 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +192 -13
- package/build/system/mcp-servers/admin/schemas.js +39 -3
- package/build/system/mcp-servers/admin/users.js +1 -0
- package/build/system/mcp-servers/docs.js +4 -0
- package/build/system/mcp-servers/human-queue-schemas.d.ts +85 -0
- package/build/system/mcp-servers/human-queue-schemas.js +19 -1
- package/build/system/mcp-servers/human-queue.d.ts +2 -0
- package/build/system/mcp-servers/human-queue.js +77 -0
- package/build/system/seed/tool-manifests-admin.d.ts +922 -103
- package/build/system/seed/tool-manifests-admin.js +4 -1
- package/build/system/seed/tool-manifests-escalation.d.ts +174 -0
- package/build/system/seed/tool-manifests-escalation.js +56 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/escalation.d.ts +52 -0
- package/build/types/escalation.js +29 -1
- package/build/types/index.d.ts +2 -2
- package/build/types/index.js +3 -1
- package/build/types/scan-code.d.ts +24 -0
- package/build/types/scan-code.js +9 -0
- package/build/types/startup.d.ts +20 -0
- package/dashboard/dist/assets/{AdminDashboard-DplvDBti.js → AdminDashboard-uarpHB88.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-DplvDBti.js.map → AdminDashboard-uarpHB88.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-D8Bf6YxO.js → AgentConfigPage-BcUaDD5E.js} +6 -6
- package/dashboard/dist/assets/{AgentConfigPage-D8Bf6YxO.js.map → AgentConfigPage-BcUaDD5E.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-BN8g0Y2G.js → AgentDetailPage-BaZBhK6i.js} +3 -3
- package/dashboard/dist/assets/{AgentDetailPage-BN8g0Y2G.js.map → AgentDetailPage-BaZBhK6i.js.map} +1 -1
- package/dashboard/dist/assets/AgentsPage-CuVFSDc4.js +2 -0
- package/dashboard/dist/assets/{AgentsPage-7JJBHku8.js.map → AgentsPage-CuVFSDc4.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-iO5NyyIK.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-iO5NyyIK.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-CGCnIz8I.js → BotPicker-CClMby6B.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-CGCnIz8I.js.map → BotPicker-CClMby6B.js.map} +1 -1
- package/dashboard/dist/assets/BulkAssignModal-BL8FRgkN.js +2 -0
- package/dashboard/dist/assets/{BulkAssignModal-NJBcoj-f.js.map → BulkAssignModal-BL8FRgkN.js.map} +1 -1
- package/dashboard/dist/assets/CapabilitiesPage-D53ogPor.js +2 -0
- package/dashboard/dist/assets/{CapabilitiesPage-F8dpNn9B.js.map → CapabilitiesPage-D53ogPor.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-BZW6VpCU.js → CollapsibleSection-ow8jpbtD.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-BZW6VpCU.js.map → CollapsibleSection-ow8jpbtD.js.map} +1 -1
- package/dashboard/dist/assets/{ConfirmDeleteModal-64adXLdq.js → ConfirmDeleteModal-CeEDt9iJ.js} +2 -2
- package/dashboard/dist/assets/{ConfirmDeleteModal-64adXLdq.js.map → ConfirmDeleteModal-CeEDt9iJ.js.map} +1 -1
- package/dashboard/dist/assets/{CountdownTimer-BxDub9d2.js → CountdownTimer-CSqyZMCG.js} +2 -2
- package/dashboard/dist/assets/{CountdownTimer-BxDub9d2.js.map → CountdownTimer-CSqyZMCG.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-CnLzopLL.js → CredentialsPage-Dx7SxA23.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-CnLzopLL.js.map → CredentialsPage-Dx7SxA23.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-CAD5UYDT.js → CronLabel-LYPxpvNN.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-CAD5UYDT.js.map → CronLabel-LYPxpvNN.js.map} +1 -1
- package/dashboard/dist/assets/DataTable-spX1dFyb.js +2 -0
- package/dashboard/dist/assets/DataTable-spX1dFyb.js.map +1 -0
- package/dashboard/dist/assets/{DropZone-XlcY8hEH.js → DropZone-f1o-GLM8.js} +2 -2
- package/dashboard/dist/assets/{DropZone-XlcY8hEH.js.map → DropZone-f1o-GLM8.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-CwO4ufd0.js → ElapsedCell-BZJTSm9N.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-CwO4ufd0.js.map → ElapsedCell-BZJTSm9N.js.map} +1 -1
- package/dashboard/dist/assets/EntityTimelinePanel-0wIIW2en.js +2 -0
- package/dashboard/dist/assets/{EntityTimelinePanel-CkF-oW-m.js.map → EntityTimelinePanel-0wIIW2en.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationListSchemaPage-h9JO_q5f.js → EscalationListSchemaPage-CoYvtMzj.js} +3 -3
- package/dashboard/dist/assets/{EscalationListSchemaPage-h9JO_q5f.js.map → EscalationListSchemaPage-CoYvtMzj.js.map} +1 -1
- package/dashboard/dist/assets/EscalationListView-DWFHPufv.js +2 -0
- package/dashboard/dist/assets/EscalationListView-DWFHPufv.js.map +1 -0
- package/dashboard/dist/assets/{EscalationSchemaPage-mJFL5_Se.js → EscalationSchemaPage-C9Xu_A25.js} +3 -3
- package/dashboard/dist/assets/{EscalationSchemaPage-mJFL5_Se.js.map → EscalationSchemaPage-C9Xu_A25.js.map} +1 -1
- package/dashboard/dist/assets/EscalationTimeline-D8W2LNNk.js +2 -0
- package/dashboard/dist/assets/EscalationTimeline-D8W2LNNk.js.map +1 -0
- package/dashboard/dist/assets/EscalationTitleSelect-DLK8NWMp.js +2 -0
- package/dashboard/dist/assets/EscalationTitleSelect-DLK8NWMp.js.map +1 -0
- package/dashboard/dist/assets/{EscalationsOverview-DlXUFqWJ.js → EscalationsOverview-CW57FaP9.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-DlXUFqWJ.js.map → EscalationsOverview-CW57FaP9.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-Ike2xchn.js → EventTable-D1ctWhKf.js} +2 -2
- package/dashboard/dist/assets/{EventTable-Ike2xchn.js.map → EventTable-D1ctWhKf.js.map} +1 -1
- package/dashboard/dist/assets/{FilterBar-DMOR-O1c.js → FilterBar-C-iur-Nj.js} +2 -2
- package/dashboard/dist/assets/{FilterBar-DMOR-O1c.js.map → FilterBar-C-iur-Nj.js.map} +1 -1
- package/dashboard/dist/assets/GraphInvokePage-CcA_44M4.js +2 -0
- package/dashboard/dist/assets/{GraphInvokePage-CKtvWowP.js.map → GraphInvokePage-CcA_44M4.js.map} +1 -1
- package/dashboard/dist/assets/HomePage-Dc085ghJ.js +2 -0
- package/dashboard/dist/assets/{HomePage-BQ3Q791Q.js.map → HomePage-Dc085ghJ.js.map} +1 -1
- package/dashboard/dist/assets/ListToolbar-h_ziBt4O.js +2 -0
- package/dashboard/dist/assets/{ListToolbar-DdJzj472.js.map → ListToolbar-h_ziBt4O.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-BbtrduWn.js → McpOverview-qXXhbNWV.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-BbtrduWn.js.map → McpOverview-qXXhbNWV.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-C1L4T-Wt.js → McpQueryDetailPage-SEzSbZrd.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-C1L4T-Wt.js.map → McpQueryDetailPage-SEzSbZrd.js.map} +1 -1
- package/dashboard/dist/assets/McpQueryPage-Cy0V0kv9.js +2 -0
- package/dashboard/dist/assets/{McpQueryPage-CfJy7HZ7.js.map → McpQueryPage-Cy0V0kv9.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-CA9yv7he.js → McpRunDetailPage-DjJO5vSb.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-CA9yv7he.js.map → McpRunDetailPage-DjJO5vSb.js.map} +1 -1
- package/dashboard/dist/assets/McpRunsPage-BfjiJ8mM.js +2 -0
- package/dashboard/dist/assets/{McpRunsPage-D7QXjW9z.js.map → McpRunsPage-BfjiJ8mM.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-Bb3revqD.js → NamespacePill-DADRJOUl.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-Bb3revqD.js.map → NamespacePill-DADRJOUl.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-dJcER0T_.js +3 -0
- package/dashboard/dist/assets/OperationsPage-dJcER0T_.js.map +1 -0
- package/dashboard/dist/assets/OperatorDashboard-zCkW4eqk.js +2 -0
- package/dashboard/dist/assets/{OperatorDashboard-sXNDh1Ph.js.map → OperatorDashboard-zCkW4eqk.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-Di5Sz3an.js → PageHeader-Csq434-v.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-Di5Sz3an.js.map → PageHeader-Csq434-v.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-DutKJ-h2.js → PageHeaderWithStats-CSnk1p5A.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-DutKJ-h2.js.map → PageHeaderWithStats-CSnk1p5A.js.map} +1 -1
- package/dashboard/dist/assets/{PersonaDetailPage-NeHD-Gra.js → PersonaDetailPage-Cx6L3Jvu.js} +2 -2
- package/dashboard/dist/assets/{PersonaDetailPage-NeHD-Gra.js.map → PersonaDetailPage-Cx6L3Jvu.js.map} +1 -1
- package/dashboard/dist/assets/{PersonasPage-DavKXRWX.js → PersonasPage-DOgGUlJl.js} +2 -2
- package/dashboard/dist/assets/{PersonasPage-DavKXRWX.js.map → PersonasPage-DOgGUlJl.js.map} +1 -1
- package/dashboard/dist/assets/PortalPage-BKuL1BeJ.js +2 -0
- package/dashboard/dist/assets/PortalPage-BKuL1BeJ.js.map +1 -0
- package/dashboard/dist/assets/{ProcessDetailPage-DkdKJGe6.js → ProcessDetailPage-BFcFSDUv.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-DkdKJGe6.js.map → ProcessDetailPage-BFcFSDUv.js.map} +1 -1
- package/dashboard/dist/assets/ProcessesListPage-BaHV2cXp.js +2 -0
- package/dashboard/dist/assets/{ProcessesListPage-CjwVKe32.js.map → ProcessesListPage-BaHV2cXp.js.map} +1 -1
- package/dashboard/dist/assets/ResolverForm-Bgc0Zrya.js +7 -0
- package/dashboard/dist/assets/ResolverForm-Bgc0Zrya.js.map +1 -0
- package/dashboard/dist/assets/RoleDetailPage-BPY6i3CU.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-BPY6i3CU.js.map +1 -0
- package/dashboard/dist/assets/{RolePill-DhnVPK4q.js → RolePill-BafrASDX.js} +2 -2
- package/dashboard/dist/assets/{RolePill-DhnVPK4q.js.map → RolePill-BafrASDX.js.map} +1 -1
- package/dashboard/dist/assets/{RolesPage-OM9KM8Pg.js → RolesPage-DSqqKPbv.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-OM9KM8Pg.js.map → RolesPage-DSqqKPbv.js.map} +1 -1
- package/dashboard/dist/assets/RunAsSelector-B29iWwel.js +2 -0
- package/dashboard/dist/assets/{RunAsSelector-fFMt1bGe.js.map → RunAsSelector-B29iWwel.js.map} +1 -1
- package/dashboard/dist/assets/{ScanCodesPage-AujH-pPQ.js → ScanCodesPage-S8aAJfnC.js} +2 -2
- package/dashboard/dist/assets/{ScanCodesPage-AujH-pPQ.js.map → ScanCodesPage-S8aAJfnC.js.map} +1 -1
- package/dashboard/dist/assets/{ScanSchemeDetailPage-eKQpmsC_.js → ScanSchemeDetailPage-CUBRAtmJ.js} +2 -2
- package/dashboard/dist/assets/{ScanSchemeDetailPage-eKQpmsC_.js.map → ScanSchemeDetailPage-CUBRAtmJ.js.map} +1 -1
- package/dashboard/dist/assets/ScanStationPage-R9t1-CeQ.js +2 -0
- package/dashboard/dist/assets/{ScanStationPage-C1_ppDoZ.js.map → ScanStationPage-R9t1-CeQ.js.map} +1 -1
- package/dashboard/dist/assets/{ScopeBadge-CXUg0FHo.js → ScopeBadge-b6MIBiYD.js} +2 -2
- package/dashboard/dist/assets/{ScopeBadge-CXUg0FHo.js.map → ScopeBadge-b6MIBiYD.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-B91Pk9xh.js → StickyPagination-CkW_A507.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-B91Pk9xh.js.map → StickyPagination-CkW_A507.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-C7SPqaJt.js → StreamMessageDetail-yzWX0wac.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-C7SPqaJt.js.map → StreamMessageDetail-yzWX0wac.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-BcHR8ylf.js → SwimlaneTimeline-Cf4DXFxj.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-BcHR8ylf.js.map → SwimlaneTimeline-Cf4DXFxj.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-BZ8vb2oW.js → TagInput-ZrIIrEUP.js} +2 -2
- package/dashboard/dist/assets/{TagInput-BZ8vb2oW.js.map → TagInput-ZrIIrEUP.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-Bqj0I_I0.js → TaskDetailPage-DCDcEps9.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-Bqj0I_I0.js.map → TaskDetailPage-DCDcEps9.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-Bxir8WRl.js → TaskQueuePill-DkQ1uc6y.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-Bxir8WRl.js.map → TaskQueuePill-DkQ1uc6y.js.map} +1 -1
- package/dashboard/dist/assets/TasksListPage-BBATgpf7.js +2 -0
- package/dashboard/dist/assets/{TasksListPage-BB1f79lL.js.map → TasksListPage-BBATgpf7.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-BFnODmNe.js → TimeAgo-CSNVEjch.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-BFnODmNe.js.map → TimeAgo-CSNVEjch.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-BXgLOBsr.js → TimestampCell-Du1nBrPn.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-BXgLOBsr.js.map → TimestampCell-Du1nBrPn.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-FkgTXIhq.js → ToolPill-Baxm02L6.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-FkgTXIhq.js.map → ToolPill-Baxm02L6.js.map} +1 -1
- package/dashboard/dist/assets/ToolTestPanel-CF-h_5Rg.js +2 -0
- package/dashboard/dist/assets/{ToolTestPanel-Bwe3J5lj.js.map → ToolTestPanel-CF-h_5Rg.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-DAF07vZ9.js → TopicDetailPage-Dr1UmWcp.js} +3 -3
- package/dashboard/dist/assets/{TopicDetailPage-DAF07vZ9.js.map → TopicDetailPage-Dr1UmWcp.js.map} +1 -1
- package/dashboard/dist/assets/TopicsPage-CUUo-Ylf.js +2 -0
- package/dashboard/dist/assets/{TopicsPage-C1gxBUap.js.map → TopicsPage-CUUo-Ylf.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-BzXJUE6q.js → UserName-kBh1dc39.js} +2 -2
- package/dashboard/dist/assets/{UserName-BzXJUE6q.js.map → UserName-kBh1dc39.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-SkRU5cRX.js → WorkflowExecutionPage-CCRSbnDx.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-SkRU5cRX.js.map → WorkflowExecutionPage-CCRSbnDx.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill--AYMW7u_.js → WorkflowPill-ZepbO_ny.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill--AYMW7u_.js.map → WorkflowPill-ZepbO_ny.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowsDashboard-BNQVvyek.js +2 -0
- package/dashboard/dist/assets/{WorkflowsDashboard-CZAqRk1N.js.map → WorkflowsDashboard-BNQVvyek.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-BoN44VTE.js → WorkflowsOverview-DswubNJN.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-BoN44VTE.js.map → WorkflowsOverview-DswubNJN.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-CqjhtdJf.js → YamlWorkflowDetailPage-Cw9Dlmup.js} +11 -11
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-CqjhtdJf.js.map → YamlWorkflowDetailPage-Cw9Dlmup.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-CwnJkzyb.js → YamlWorkflowsPage-DB8xnIXy.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-CwnJkzyb.js.map → YamlWorkflowsPage-DB8xnIXy.js.map} +1 -1
- package/dashboard/dist/assets/{agents-gV0Ys-sY.js → agents-BwEnf6N9.js} +2 -2
- package/dashboard/dist/assets/{agents-gV0Ys-sY.js.map → agents-BwEnf6N9.js.map} +1 -1
- package/dashboard/dist/assets/{bots-B97itB3I.js → bots-CCmJTT2u.js} +2 -2
- package/dashboard/dist/assets/{bots-B97itB3I.js.map → bots-CCmJTT2u.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-CR9xZgGd.js → capabilities-BZHgp_nh.js} +2 -2
- package/dashboard/dist/assets/{capabilities-CR9xZgGd.js.map → capabilities-BZHgp_nh.js.map} +1 -1
- package/dashboard/dist/assets/{constants-BHkpVaqx.js → constants-CeBBTzf5.js} +2 -2
- package/dashboard/dist/assets/{constants-BHkpVaqx.js.map → constants-CeBBTzf5.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-YxvP1KCa.js → controlplane-CyfwUlFA.js} +2 -2
- package/dashboard/dist/assets/{controlplane-YxvP1KCa.js.map → controlplane-CyfwUlFA.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-BJPTNmLW.js +2 -0
- package/dashboard/dist/assets/escalation-columns-BJPTNmLW.js.map +1 -0
- package/dashboard/dist/assets/index-3gbd5neL.js +67 -0
- package/dashboard/dist/assets/index-3gbd5neL.js.map +1 -0
- package/dashboard/dist/assets/index-BAAVTyZ-.js +2 -0
- package/dashboard/dist/assets/{index-BQpV-DTG.js.map → index-BAAVTyZ-.js.map} +1 -1
- package/dashboard/dist/assets/{index-B9BBCqLI.js → index-BC6Jenlo.js} +2 -2
- package/dashboard/dist/assets/{index-B9BBCqLI.js.map → index-BC6Jenlo.js.map} +1 -1
- package/dashboard/dist/assets/index-C7KHqOTQ.js +2 -0
- package/dashboard/dist/assets/{index-Bp9KBLT5.js.map → index-C7KHqOTQ.js.map} +1 -1
- package/dashboard/dist/assets/index-CDrqqbSR.js +3 -0
- package/dashboard/dist/assets/index-CDrqqbSR.js.map +1 -0
- package/dashboard/dist/assets/index-CVwzCZ43.js +2 -0
- package/dashboard/dist/assets/{index-CFbo9FU9.js.map → index-CVwzCZ43.js.map} +1 -1
- package/dashboard/dist/assets/index-CaGF6TCC.js +2 -0
- package/dashboard/dist/assets/index-CaGF6TCC.js.map +1 -0
- package/dashboard/dist/assets/index-ClVbfe4z.js +13 -0
- package/dashboard/dist/assets/{index-Cfvkl2J0.js.map → index-ClVbfe4z.js.map} +1 -1
- package/dashboard/dist/assets/{index-DMeaiKgf.js → index-CuVkSmj5.js} +2 -2
- package/dashboard/dist/assets/{index-DMeaiKgf.js.map → index-CuVkSmj5.js.map} +1 -1
- package/dashboard/dist/assets/{index-ipqwPavs.js → index-DAuX_PlL.js} +2 -2
- package/dashboard/dist/assets/{index-ipqwPavs.js.map → index-DAuX_PlL.js.map} +1 -1
- package/dashboard/dist/assets/{index-CIpHj4di.js → index-DBWS5QL8.js} +2 -2
- package/dashboard/dist/assets/{index-CIpHj4di.js.map → index-DBWS5QL8.js.map} +1 -1
- package/dashboard/dist/assets/index-DIWCI6HC.js +2 -0
- package/dashboard/dist/assets/{index-CUZ6LYye.js.map → index-DIWCI6HC.js.map} +1 -1
- package/dashboard/dist/assets/{index-DwOnWeu8.js → index-DZ8X_Gp4.js} +2 -2
- package/dashboard/dist/assets/{index-DwOnWeu8.js.map → index-DZ8X_Gp4.js.map} +1 -1
- package/dashboard/dist/assets/index-Dh8MJl3L.css +1 -0
- package/dashboard/dist/assets/{knowledge-XX6cW4Ob.js → knowledge-CqyLhUMg.js} +2 -2
- package/dashboard/dist/assets/{knowledge-XX6cW4Ob.js.map → knowledge-CqyLhUMg.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-CH13hNH2.js → mcp-Cvu6bFYH.js} +2 -2
- package/dashboard/dist/assets/{mcp-CH13hNH2.js.map → mcp-Cvu6bFYH.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-Duydd3JA.js → mcp-query-qC6A3e9l.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-Duydd3JA.js.map → mcp-query-qC6A3e9l.js.map} +1 -1
- package/dashboard/dist/assets/metadata-display-COo3mMCr.js +2 -0
- package/dashboard/dist/assets/metadata-display-COo3mMCr.js.map +1 -0
- package/dashboard/dist/assets/{personas-mDk4lLQS.js → personas-BKN_lJuV.js} +2 -2
- package/dashboard/dist/assets/{personas-mDk4lLQS.js.map → personas-BKN_lJuV.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-B5GcgCeF.js → pipelines-BldIfv85.js} +2 -2
- package/dashboard/dist/assets/{pipelines-B5GcgCeF.js.map → pipelines-BldIfv85.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-CItmNsRp.js → tasks-B-Uay8uL.js} +2 -2
- package/dashboard/dist/assets/{tasks-CItmNsRp.js.map → tasks-B-Uay8uL.js.map} +1 -1
- package/dashboard/dist/assets/{topics-CJYG7BX5.js → topics-438EQogt.js} +2 -2
- package/dashboard/dist/assets/{topics-CJYG7BX5.js.map → topics-438EQogt.js.map} +1 -1
- package/dashboard/dist/assets/useContainerWidth-DSLp1gEm.js +2 -0
- package/dashboard/dist/assets/useContainerWidth-DSLp1gEm.js.map +1 -0
- package/dashboard/dist/assets/{useDebouncedValue-BrwgXcp8.js → useDebouncedValue-Cw8hYZ8y.js} +2 -2
- package/dashboard/dist/assets/{useDebouncedValue-BrwgXcp8.js.map → useDebouncedValue-Cw8hYZ8y.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-CHty0HAS.js → useNamespace-D1218GQ8.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-CHty0HAS.js.map → useNamespace-D1218GQ8.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-CoTsia7t.js → useYamlActivityEvents-BAQf_y_U.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-CoTsia7t.js.map → useYamlActivityEvents-BAQf_y_U.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-DeBdvyKJ.js → vendor-icons-B7OcUM6b.js} +2 -2
- package/dashboard/dist/assets/{vendor-icons-DeBdvyKJ.js.map → vendor-icons-B7OcUM6b.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-CiWwx5rM.js → yaml-workflows-CN01gEB0.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-CiWwx5rM.js.map → yaml-workflows-CN01gEB0.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/accumulator.md +113 -0
- package/docs/api/http/escalations.md +89 -0
- package/docs/api/http/roles.md +1 -0
- package/docs/api/sdk/escalations.md +91 -0
- package/docs/api/sdk/roles.md +2 -0
- package/docs/architecture.md +1 -1
- package/docs/code-owned-configuration.md +2 -1
- package/docs/dashboard.md +10 -4
- package/docs/hitl/design.md +1 -1
- package/docs/hitl/escalation.md +37 -1
- package/docs/hitl/invoke-form.md +4 -0
- package/docs/hitl/pinned-views.md +4 -0
- package/docs/hitl/portal.md +97 -0
- package/docs/hitl/resolution.md +41 -0
- package/docs/hitl/x-lt-embed.md +1 -1
- package/docs/hitl/x-lt-invoke.md +95 -0
- package/docs/hitl/x-lt-list-schema.md +3 -1
- package/docs/hitl/x-lt-widget.md +1 -0
- package/docs/hitl-guide.md +8 -1
- package/docs/scan-codes.md +15 -2
- package/docs/sdk.md +1 -1
- package/docs/workflows.md +2 -2
- package/package.json +4 -2
- package/dashboard/dist/assets/AgentsPage-7JJBHku8.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-lz5qwSjg.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-lz5qwSjg.js.map +0 -1
- package/dashboard/dist/assets/BulkAssignModal-NJBcoj-f.js +0 -2
- package/dashboard/dist/assets/CapabilitiesPage-F8dpNn9B.js +0 -2
- package/dashboard/dist/assets/DataTable-7C7riavf.js +0 -2
- package/dashboard/dist/assets/DataTable-7C7riavf.js.map +0 -1
- package/dashboard/dist/assets/EntityTimelinePanel-CkF-oW-m.js +0 -2
- package/dashboard/dist/assets/EscalationListView-AHW2xtlr.js +0 -2
- package/dashboard/dist/assets/EscalationListView-AHW2xtlr.js.map +0 -1
- package/dashboard/dist/assets/GraphInvokePage-CKtvWowP.js +0 -2
- package/dashboard/dist/assets/HomePage-BQ3Q791Q.js +0 -2
- package/dashboard/dist/assets/ListToolbar-DdJzj472.js +0 -2
- package/dashboard/dist/assets/McpQueryPage-CfJy7HZ7.js +0 -2
- package/dashboard/dist/assets/McpRunsPage-D7QXjW9z.js +0 -2
- package/dashboard/dist/assets/OperationsPage-aLHHAP2X.js +0 -3
- package/dashboard/dist/assets/OperationsPage-aLHHAP2X.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-sXNDh1Ph.js +0 -2
- package/dashboard/dist/assets/ProcessesListPage-CjwVKe32.js +0 -2
- package/dashboard/dist/assets/ResolverForm-Bk-wDkN-.js +0 -3
- package/dashboard/dist/assets/ResolverForm-Bk-wDkN-.js.map +0 -1
- package/dashboard/dist/assets/RoleDetailPage-WQt0y5l7.js +0 -8
- package/dashboard/dist/assets/RoleDetailPage-WQt0y5l7.js.map +0 -1
- package/dashboard/dist/assets/RunAsSelector-fFMt1bGe.js +0 -2
- package/dashboard/dist/assets/ScanStationPage-C1_ppDoZ.js +0 -2
- package/dashboard/dist/assets/TasksListPage-BB1f79lL.js +0 -2
- package/dashboard/dist/assets/ToolTestPanel-Bwe3J5lj.js +0 -2
- package/dashboard/dist/assets/TopicsPage-C1gxBUap.js +0 -2
- package/dashboard/dist/assets/WorkflowsDashboard-CZAqRk1N.js +0 -2
- package/dashboard/dist/assets/escalation-columns-uq-IV2aj.js +0 -2
- package/dashboard/dist/assets/escalation-columns-uq-IV2aj.js.map +0 -1
- package/dashboard/dist/assets/index-3ezfWLkE.js +0 -6
- package/dashboard/dist/assets/index-3ezfWLkE.js.map +0 -1
- package/dashboard/dist/assets/index-BQpV-DTG.js +0 -2
- package/dashboard/dist/assets/index-Bp9KBLT5.js +0 -2
- package/dashboard/dist/assets/index-Bpvfr4OW.css +0 -1
- package/dashboard/dist/assets/index-CFbo9FU9.js +0 -2
- package/dashboard/dist/assets/index-CIwlVfvV.js +0 -67
- package/dashboard/dist/assets/index-CIwlVfvV.js.map +0 -1
- package/dashboard/dist/assets/index-CUZ6LYye.js +0 -2
- package/dashboard/dist/assets/index-Cfvkl2J0.js +0 -13
- package/dashboard/dist/assets/index-SGyrXGp8.js +0 -3
- package/dashboard/dist/assets/index-SGyrXGp8.js.map +0 -1
- package/dashboard/dist/assets/invoke-context-DrVyUEfs.js +0 -2
- package/dashboard/dist/assets/invoke-context-DrVyUEfs.js.map +0 -1
- package/dashboard/dist/assets/metadata-display--TzK0REC.js +0 -2
- package/dashboard/dist/assets/metadata-display--TzK0REC.js.map +0 -1
- package/dashboard/dist/assets/resolver-payload-m9AGKZ3a.js +0 -2
- package/dashboard/dist/assets/resolver-payload-m9AGKZ3a.js.map +0 -1
- package/docs/compilation.md +0 -152
- package/docs/compiler.md +0 -311
- package/docs/contributing.md +0 -56
- package/docs/design-principles.md +0 -245
- package/docs/schema-exchange.md +0 -173
- package/docs/self-test.md +0 -106
- package/docs/story.md +0 -184
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-CDrqqbSR.js","sources":["../../src/components/scan/ScanConfirmModal.tsx","../../src/components/common/display/EscalationTimeline.tsx","../../src/components/escalation/TriageContext.tsx","../../src/lib/escalation-items.ts","../../src/components/escalation/EscalationItemsPanel.tsx","../../src/components/escalation/EscalationSidePanel.tsx","../../src/pages/operator/escalation-detail/EscalationActionBar.tsx","../../src/components/escalation/RoundsExhaustedContext.tsx","../../src/lib/x-lt-viewport.ts","../../src/components/escalation/IframeViewport.tsx","../../src/pages/operator/escalation-detail/ResolverSection.tsx","../../src/pages/operator/escalation-detail/EscalationDetailSections.tsx","../../src/pages/operator/escalation-detail/ClaimExpiryModal.tsx","../../src/hooks/useClaimClock.ts","../../src/lib/draft-store.ts","../../../shared/form-validation/x-lt-transition.ts","../../../shared/form-validation/x-lt-submit-on-claim.ts","../../../shared/form-validation/x-lt-submit-guard.ts","../../src/hooks/useSubmitGuard.ts","../../src/components/escalation/TransitionWaitModal.tsx","../../src/components/scan/station/StationWriteChallenge.tsx","../../src/pages/operator/escalation-detail/EscalationDetailPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { Modal } from '../common/modal/Modal';\nimport { apiFetch } from '../../api/client';\nimport { SCAN_VERBS, type ScanPendingAction } from '../../api/scan-codes';\nimport { SCAN_PENDING_ACTION_STATE } from '../../hooks/useScanInput';\n\n/**\n * Second phase of a confirmed scan action. The scan located the escalation\n * and navigated here with the pending action in route state; this modal asks\n * the rule's prompt and, on confirm, fires the standard per-id endpoint —\n * the same guarded calls every other surface uses.\n */\nexport function ScanConfirmModal({ escalationId }: { escalationId: string }) {\n const location = useLocation();\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const pending = (location.state as Record<string, unknown> | null)\n ?.[SCAN_PENDING_ACTION_STATE] as ScanPendingAction | undefined;\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [dismissed, setDismissed] = useState(false);\n\n if (!pending || pending.escalationId !== escalationId || dismissed) return null;\n\n const clearState = () => {\n setDismissed(true);\n // Drop the pending action from history so refresh/back never re-prompts.\n navigate(location.pathname, { replace: true, state: null });\n };\n\n const confirm = async () => {\n setBusy(true);\n setError(null);\n try {\n switch (pending.verb) {\n case SCAN_VERBS.CANCEL:\n await apiFetch(`/escalations/${escalationId}/cancel`, { method: 'POST' });\n break;\n case SCAN_VERBS.CLAIM:\n case SCAN_VERBS.CLAIM_SHOW_DETAIL:\n await apiFetch(`/escalations/${escalationId}/claim`, {\n method: 'POST',\n body: JSON.stringify({ durationMinutes: pending.params?.durationMinutes ?? 30 }),\n });\n break;\n case SCAN_VERBS.RELEASE:\n await apiFetch(`/escalations/${escalationId}/release`, { method: 'POST' });\n break;\n case SCAN_VERBS.RESOLVE:\n await apiFetch(`/escalations/${escalationId}/resolve`, {\n method: 'POST',\n body: JSON.stringify({ resolverPayload: pending.params?.resolverPayload ?? {} }),\n });\n break;\n case SCAN_VERBS.ESCALATE:\n await apiFetch(`/escalations/${escalationId}/escalate`, {\n method: 'PATCH',\n body: JSON.stringify({ targetRole: pending.params?.targetRole }),\n });\n break;\n default:\n throw new Error(`Unsupported confirm verb \"${pending.verb}\"`);\n }\n queryClient.resetQueries({ queryKey: ['escalations'] });\n queryClient.resetQueries({ queryKey: ['escalationStats'] });\n clearState();\n } catch (err: any) {\n setError(err.message);\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <Modal open onClose={clearState} title=\"Confirm scan action\">\n <div className=\"space-y-4\">\n <p className=\"text-sm text-text-secondary\">{pending.prompt}</p>\n {error && <p className=\"text-xs text-status-error\">{error}</p>}\n <div className=\"flex justify-end gap-3 pt-2\">\n <button onClick={clearState} disabled={busy} className=\"btn-secondary text-xs\">\n No, go back\n </button>\n <button onClick={confirm} disabled={busy} className=\"btn-primary text-xs\">\n {busy ? 'Working…' : 'Yes, continue'}\n </button>\n </div>\n </div>\n </Modal>\n );\n}\n","import { formatDurationCompact } from '../../../lib/format';\nimport { isEffectivelyClaimed } from '../../../lib/escalation';\nimport type { LTEscalationRecord } from '../../../api/types';\n\n// Matches the palette used in EscalationTimeline (the spine view) and PaceChart.\n// Hues resolve through the --lt-* status tokens so registered themes restyle\n// the bar; the navy default keeps the sky/orange/green/red look.\nconst COLORS = {\n pending: 'rgb(var(--lt-status-queued-graphic))', // sky blue — waiting to be claimed\n claimed: 'rgb(var(--lt-status-claimed-graphic))', // orange — actively being worked\n resolved: 'rgb(var(--lt-status-success-graphic))', // green — done\n cancelled: 'rgb(var(--lt-status-error))', // red — cancelled or expired\n} as const;\n\nconst BAR_H = 4; // px\n\n/** A duration label tethered to a point on the bar by a thin vertical tick. */\nfunction Marker({ pct, place, label, title, emphasis }: {\n pct: number;\n place: 'above' | 'below';\n label: string;\n title: string;\n emphasis?: boolean;\n}) {\n const tick = <span className=\"w-px h-1 bg-text-quaternary/70\" aria-hidden />;\n const text = (\n <span className={`whitespace-nowrap leading-none tabular-nums text-[10px] ${emphasis ? 'text-text-tertiary' : 'text-text-quaternary'}`}>\n {label}\n </span>\n );\n return (\n <div\n title={title}\n className={`absolute ${place === 'above' ? 'bottom-0' : 'top-0'} flex flex-col items-center gap-0 -translate-x-1/2 cursor-help`}\n style={{ left: `${pct}%` }}\n >\n {place === 'above' ? <>{text}{tick}</> : <>{tick}{text}</>}\n </div>\n );\n}\n\n/**\n * Compact lifecycle sparkline for the escalation detail side panel.\n *\n * Bar segments: blue (pending — waiting to be claimed) → orange (claimed —\n * being worked) → green (resolved) or red (cancelled). Markers show\n * time-to-claim above the split point and total duration below the right edge.\n */\nexport function EscalationTimeline({ esc, className = '' }: { esc: LTEscalationRecord; className?: string }) {\n const created = new Date(esc.created_at).getTime();\n const claimedAt = esc.claimed_at ? new Date(esc.claimed_at).getTime() : null;\n\n const isResolved = esc.status === 'resolved';\n const isCancelled = esc.status === 'cancelled' || esc.status === 'expired';\n const isTerminal = isResolved || isCancelled;\n const activeClaim = isEffectivelyClaimed(esc);\n\n const showClaimed = !!claimedAt && (activeClaim || isTerminal);\n\n const end = isResolved && esc.resolved_at\n ? new Date(esc.resolved_at).getTime()\n : isCancelled\n ? new Date(esc.updated_at).getTime()\n : Date.now();\n\n const total = Math.max(end - created, 1);\n const pendingMs = showClaimed && claimedAt ? Math.max(claimedAt - created, 0) : total;\n const activeMs = showClaimed && claimedAt ? Math.max(end - claimedAt, 0) : 0;\n\n const pendingPct = Math.min(100, Math.max(0, (pendingMs / total) * 100));\n const activePct = showClaimed ? Math.max(0, 100 - pendingPct) : 0;\n\n const activeColor = isResolved ? COLORS.resolved : isCancelled ? COLORS.cancelled : COLORS.claimed;\n const activeVerb = isResolved ? 'Resolved after' : isCancelled ? 'Cancelled after' : 'In progress';\n\n const split = formatDurationCompact(pendingMs);\n const totalStr = formatDurationCompact(total);\n const totalTip = isResolved\n ? `Resolved in ${totalStr} total`\n : isCancelled\n ? `Cancelled after ${totalStr}`\n : `Open ${totalStr} so far`;\n\n const legend = `Lifecycle bar: blue = time waiting to be claimed, ${isResolved ? 'green' : isCancelled ? 'red' : 'orange'} = ${activeVerb.toLowerCase()} the claim.`;\n\n return (\n <div className={className} title={legend}>\n {/* claim split label (above bar) */}\n <div className=\"relative h-2.5 font-mono\">\n {showClaimed && (\n <Marker\n pct={pendingPct}\n place=\"above\"\n label={split}\n title={`Time to claim: ${split}`}\n />\n )}\n </div>\n\n {/* Bar track */}\n <div\n className=\"flex w-full rounded-full overflow-hidden\"\n style={{ height: BAR_H, backgroundColor: 'rgb(var(--lt-surface-sunken))' }}\n title={legend}\n >\n {/* Pending (blue) */}\n <div\n style={{ width: `${pendingPct}%`, minWidth: BAR_H, backgroundColor: COLORS.pending, opacity: 0.75 }}\n title={`Waiting ${split} — created → claimed`}\n />\n {/* Claimed / resolved / cancelled */}\n {showClaimed && activePct > 0 && (\n <div\n style={{ width: `${activePct}%`, minWidth: BAR_H, backgroundColor: activeColor, opacity: 0.75 }}\n title={`${activeVerb} ${formatDurationCompact(activeMs)}`}\n />\n )}\n </div>\n\n {/* Total / age label (below bar, anchored at right edge) */}\n <div className=\"relative h-2.5 font-mono\">\n <Marker pct={100} place=\"below\" emphasis label={totalStr} title={totalTip} />\n </div>\n </div>\n );\n}\n","interface TriageData {\n diagnosis?: string;\n actions_taken?: string[];\n tool_calls_made?: number;\n confidence?: number;\n recommendation?: string;\n correctedData?: Record<string, unknown>;\n originalData?: Record<string, unknown>;\n}\n\ntype Diff = { key: string; label: string; original: unknown; corrected: unknown };\n\n/**\n * Renders a triage summary when escalation payload contains `_triage`.\n *\n * Fully generic — no knowledge of workflow types or field semantics.\n * Diffs `_triage.originalData` against the payload to detect changes.\n * Follows the unboxed section pattern used throughout the dashboard.\n */\nexport function TriageContext({ triage, payload }: {\n triage: TriageData;\n payload: Record<string, unknown>;\n}) {\n const confidence = triage.confidence ?? 0;\n const confidenceColor =\n confidence >= 0.8 ? 'text-status-success' :\n confidence >= 0.5 ? 'text-status-pending' :\n 'text-status-error';\n\n // Generic diff: compare originalData fields to current payload\n const diffs = computeDiffs(triage.originalData, payload);\n\n return (\n <div className=\"space-y-5\">\n {/* Diagnosis + metadata — unboxed, natural flow */}\n {triage.diagnosis && (\n <p className=\"text-sm text-text-primary leading-relaxed\">\n {triage.diagnosis}\n </p>\n )}\n\n <div className=\"flex flex-wrap items-center gap-3 text-xs text-text-tertiary\">\n {triage.tool_calls_made != null && (\n <span>{triage.tool_calls_made} tool call{triage.tool_calls_made !== 1 ? 's' : ''}</span>\n )}\n {triage.tool_calls_made != null && <span className=\"text-text-quaternary\">·</span>}\n <span className={`font-medium ${confidenceColor}`}>\n {Math.round(confidence * 100)}% confidence\n </span>\n </div>\n\n {triage.actions_taken && triage.actions_taken.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\">\n {triage.actions_taken.map((action, i) => (\n <span\n key={i}\n className=\"inline-flex items-center gap-1.5 px-2 py-0.5 text-2xs\n text-text-secondary\"\n >\n <svg className=\"w-3 h-3 text-status-success shrink-0\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n {action}\n </span>\n ))}\n </div>\n )}\n\n {triage.recommendation && (\n <p className=\"text-2xs text-text-tertiary italic\">\n {triage.recommendation}\n </p>\n )}\n\n {/* Generic before/after diffs — separated by subtle dividers */}\n {diffs.length > 0 && (\n <div className=\"space-y-4 pt-2\">\n {diffs.map((diff) => (\n <DiffRow key={diff.key} diff={diff} />\n ))}\n </div>\n )}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Generic diff computation\n// ---------------------------------------------------------------------------\n\nfunction computeDiffs(\n original: Record<string, unknown> | undefined,\n current: Record<string, unknown>,\n): Diff[] {\n if (!original) return [];\n\n const diffs: Diff[] = [];\n for (const key of Object.keys(original)) {\n if (key.startsWith('_')) continue;\n const origVal = original[key];\n const currVal = current[key];\n if (currVal === undefined) continue;\n\n if (JSON.stringify(origVal) !== JSON.stringify(currVal)) {\n diffs.push({\n key,\n label: key.replace(/[_-]/g, ' '),\n original: origVal,\n corrected: currVal,\n });\n }\n }\n return diffs;\n}\n\n// ---------------------------------------------------------------------------\n// Diff row — clean, unboxed before/after\n// ---------------------------------------------------------------------------\n\nfunction DiffRow({ diff }: { diff: Diff }) {\n const origStr = renderValue(diff.original);\n const corrStr = renderValue(diff.corrected);\n\n // Both are primitives — side by side when the container allows. This view\n // renders inside the side panel, so the container is what matters.\n if (origStr !== null && corrStr !== null) {\n return (\n <div className=\"@container\">\n <div className=\"grid grid-cols-1 @form-cols:grid-cols-2 gap-x-8 gap-y-3\">\n <div>\n <p className=\"text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-1\">\n {diff.label} <span className=\"normal-case font-normal\">(original)</span>\n </p>\n <p className=\"text-sm text-text-tertiary leading-relaxed line-through decoration-text-tertiary/30\">\n {origStr}\n </p>\n </div>\n <div>\n <p className=\"text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-1\">\n {diff.label} <span className=\"normal-case font-normal text-status-success\">(corrected)</span>\n </p>\n <p className=\"text-sm text-text-primary leading-relaxed\">\n {corrStr}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n // Complex values — stacked\n return (\n <div className=\"space-y-3\">\n <div>\n <p className=\"text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-1\">\n {diff.label} <span className=\"normal-case font-normal\">(original)</span>\n </p>\n <pre className=\"text-xs text-text-tertiary font-mono whitespace-pre-wrap leading-relaxed\">\n {typeof diff.original === 'object' ? JSON.stringify(diff.original, null, 2) : String(diff.original)}\n </pre>\n </div>\n <div>\n <p className=\"text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-1\">\n {diff.label} <span className=\"normal-case font-normal text-status-success\">(corrected)</span>\n </p>\n <pre className=\"text-xs text-text-primary font-mono whitespace-pre-wrap leading-relaxed\">\n {typeof diff.corrected === 'object' ? JSON.stringify(diff.corrected, null, 2) : String(diff.corrected)}\n </pre>\n </div>\n </div>\n );\n}\n\n/** Render a value as a string if it's a primitive, null otherwise */\nfunction renderValue(val: unknown): string | null {\n if (val === null || val === undefined) return 'null';\n if (typeof val === 'string') return val;\n if (typeof val === 'number' || typeof val === 'boolean') return String(val);\n return null;\n}\n","import type { LTEscalationRecord } from '../api/types';\n\n/** One held item of an accumulator or batch row, in the shape the workflow receives. */\nexport interface EscalationItem {\n itemKey: string;\n payload?: Record<string, unknown>;\n /** ISO-8601 from the database clock; empty for a batch item with no stamp. */\n at: string;\n actor?: string;\n reciprocalId?: string;\n}\n\nexport interface EscalationItems {\n kind: 'accumulate' | 'batch';\n count: number;\n /** The count trigger (accumulate) or the declared size (batch); null when unbounded. */\n max: number | null;\n items: EscalationItem[];\n /** Batch only: declared keys still awaiting submission. */\n pending: string[];\n}\n\nconst ACCUMULATE_ITEMS = 'accumulate_items';\nconst ACCUMULATE_MAX = 'accumulate_max';\nconst BATCH_ITEMS = 'batch_items';\nconst BATCH_FILLED_AT = 'batch_filled_at';\nconst BATCH_KEYS = 'batch_keys';\nconst BATCH_PENDING = 'batch_pending';\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction byArrival(a: EscalationItem, b: EscalationItem): number {\n return a.at.localeCompare(b.at) || a.itemKey.localeCompare(b.itemKey);\n}\n\n/**\n * The held items of a row, or null when the row is neither an accumulator\n * nor a batch. Mirrors the server's items view so the panel and the API\n * agree on order (arrival, then key).\n */\nexport function escalationItems(\n esc: Pick<LTEscalationRecord, 'metadata'>,\n envelope: Record<string, unknown> | null,\n): EscalationItems | null {\n const metadata = (esc.metadata ?? {}) as Record<string, unknown>;\n const store = envelope?.[ACCUMULATE_ITEMS];\n if (isRecord(store)) {\n const items = Object.entries(store).map(([itemKey, entry]) => {\n const e = isRecord(entry) ? entry : {};\n return {\n itemKey,\n at: typeof e.at === 'string' ? e.at : '',\n ...(isRecord(e.payload) ? { payload: e.payload } : {}),\n ...(typeof e.actor === 'string' ? { actor: e.actor } : {}),\n ...(typeof e.reciprocalId === 'string' ? { reciprocalId: e.reciprocalId } : {}),\n } as EscalationItem;\n }).sort(byArrival);\n const max = metadata[ACCUMULATE_MAX];\n return { kind: 'accumulate', count: items.length, max: typeof max === 'number' ? max : null, items, pending: [] };\n }\n const pending = metadata[BATCH_PENDING];\n if (Array.isArray(pending)) {\n const filled = isRecord(envelope?.[BATCH_ITEMS]) ? (envelope![BATCH_ITEMS] as Record<string, unknown>) : {};\n const stamps = isRecord(envelope?.[BATCH_FILLED_AT]) ? (envelope![BATCH_FILLED_AT] as Record<string, unknown>) : {};\n const items = Object.entries(filled).map(([itemKey, payload]) => ({\n itemKey,\n at: typeof stamps[itemKey] === 'string' ? (stamps[itemKey] as string) : '',\n ...(isRecord(payload) ? { payload } : {}),\n } as EscalationItem)).sort(byArrival);\n const keys = metadata[BATCH_KEYS];\n return {\n kind: 'batch',\n count: items.length,\n max: Array.isArray(keys) ? keys.length : null,\n items,\n pending: pending.filter((k): k is string => typeof k === 'string'),\n };\n }\n return null;\n}\n\n/** A one-line summary of an item payload for the list row. */\nexport function summarizePayload(payload: Record<string, unknown> | undefined): string {\n if (!payload) return '';\n const parts = Object.entries(payload).slice(0, 3).map(([k, v]) => {\n const text = typeof v === 'object' && v !== null ? JSON.stringify(v) : String(v);\n return `${k}: ${text.length > 24 ? `${text.slice(0, 23)}…` : text}`;\n });\n const more = Object.keys(payload).length - parts.length;\n return more > 0 ? `${parts.join(' · ')} · +${more}` : parts.join(' · ');\n}\n","import { useState } from 'react';\nimport { Link } from 'react-router-dom';\nimport { Plus, X, User } from 'lucide-react';\nimport { useAccumulateItem, useRemoveAccumulatedItem } from '../../api/escalations';\nimport { DateValue } from '../common/display/DateValue';\nimport { UserName } from '../common/display/UserName';\nimport { summarizePayload, type EscalationItems } from '../../lib/escalation-items';\n\n/**\n * The held items of an accumulator or batch row, in arrival order, with the\n * count against its max. On a pending accumulator the actor can add an item\n * (key plus optional JSON payload) or remove one; both go through the same\n * guarded statements the API exposes, so a stale panel gets the server's\n * answer rather than a local guess.\n */\nexport function EscalationItemsPanel({ escalationId, items, canWrite }: {\n escalationId: string;\n items: EscalationItems;\n canWrite: boolean;\n}) {\n const add = useAccumulateItem();\n const remove = useRemoveAccumulatedItem();\n const [itemKey, setItemKey] = useState('');\n const [payloadText, setPayloadText] = useState('');\n const [formError, setFormError] = useState<string | null>(null);\n\n const writable = canWrite && items.kind === 'accumulate';\n const isFull = items.max !== null && items.count >= items.max;\n\n const submit = () => {\n const key = itemKey.trim();\n if (!key) { setFormError('An item key is required.'); return; }\n let payload: Record<string, unknown> | undefined;\n if (payloadText.trim()) {\n try {\n const parsed = JSON.parse(payloadText);\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('object');\n payload = parsed;\n } catch {\n setFormError('Payload must be a JSON object.');\n return;\n }\n }\n setFormError(null);\n add.mutate({ id: escalationId, itemKey: key, payload }, {\n onSuccess: () => { setItemKey(''); setPayloadText(''); },\n });\n };\n\n const noun = items.kind === 'batch' ? 'filled' : 'held';\n const headline = items.max === null\n ? `${items.count} ${noun}`\n : `${items.count} of ${items.max} ${noun}`;\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-xs font-medium text-text-primary\" data-testid=\"items-headline\">{headline}</span>\n <span className=\"text-2xs uppercase tracking-wide text-text-tertiary\">\n {items.kind === 'batch' ? 'Batch' : items.max === null ? 'Open' : 'Accumulator'}\n </span>\n </div>\n\n {items.items.length === 0 ? (\n <p className=\"text-xs text-text-tertiary italic\">Nothing held yet.</p>\n ) : (\n <ol className=\"divide-y divide-surface-border/60\" data-testid=\"items-list\">\n {items.items.map((item, index) => (\n <li key={item.itemKey} className=\"py-2 flex items-start gap-2 group\">\n <span className=\"text-2xs text-text-quaternary font-mono w-5 shrink-0 text-right pt-0.5\">{index + 1}</span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2 min-w-0\">\n <span className=\"text-xs font-mono text-text-primary truncate\" title={item.itemKey}>{item.itemKey}</span>\n {item.reciprocalId && (\n <Link\n to={`/escalations/detail/${item.reciprocalId}`}\n className=\"icon-link text-2xs shrink-0\"\n title=\"Open the reciprocal escalation\"\n >\n linked\n </Link>\n )}\n </div>\n {item.payload && (\n <div className=\"text-2xs text-text-secondary truncate\" title={JSON.stringify(item.payload)}>\n {summarizePayload(item.payload)}\n </div>\n )}\n <div className=\"flex items-center gap-2 text-2xs text-text-tertiary\">\n {item.at && <DateValue date={item.at} />}\n {item.actor && (\n <span className=\"inline-flex items-center gap-1\">\n <User className=\"w-2.5 h-2.5\" />\n <UserName userId={item.actor} />\n </span>\n )}\n </div>\n </div>\n {writable && (\n <button\n type=\"button\"\n onClick={() => remove.mutate({ id: escalationId, itemKey: item.itemKey })}\n disabled={remove.isPending}\n className=\"icon-link opacity-50 group-hover:opacity-100 shrink-0 p-0.5\"\n title={`Remove ${item.itemKey}`}\n aria-label={`Remove ${item.itemKey}`}\n >\n <X className=\"w-3 h-3\" />\n </button>\n )}\n </li>\n ))}\n </ol>\n )}\n\n {items.kind === 'batch' && items.pending.length > 0 && (\n <p className=\"text-2xs text-text-tertiary\">\n Awaiting: <span className=\"font-mono\">{items.pending.join(', ')}</span>\n </p>\n )}\n\n {writable && (\n <form\n className=\"space-y-2 pt-2 border-t border-surface-border/60\"\n onSubmit={(e) => { e.preventDefault(); submit(); }}\n data-testid=\"add-item-form\"\n >\n <label className=\"block text-2xs uppercase tracking-wide text-text-secondary\">Add item</label>\n <input\n value={itemKey}\n onChange={(e) => setItemKey(e.target.value)}\n placeholder=\"Item key\"\n aria-label=\"Item key\"\n disabled={isFull || add.isPending}\n className=\"w-full text-xs font-mono px-2 py-1.5 rounded-md bg-surface-base border border-surface-border text-text-primary placeholder:text-text-quaternary focus:outline-none focus:border-accent disabled:opacity-50\"\n />\n <textarea\n value={payloadText}\n onChange={(e) => setPayloadText(e.target.value)}\n placeholder='Payload JSON (optional), e.g. {\"weight\": 2}'\n aria-label=\"Item payload\"\n rows={2}\n disabled={isFull || add.isPending}\n className=\"w-full text-xs font-mono px-2 py-1.5 rounded-md bg-surface-base border border-surface-border text-text-primary placeholder:text-text-quaternary focus:outline-none focus:border-accent disabled:opacity-50\"\n />\n <div className=\"flex items-center justify-end gap-3\">\n {isFull && <span className=\"text-2xs text-text-tertiary\">Every slot is held.</span>}\n <button type=\"submit\" disabled={isFull || add.isPending} className=\"btn-secondary text-xs inline-flex items-center gap-1\">\n <Plus className=\"w-3 h-3\" />\n {add.isPending ? 'Adding…' : 'Add'}\n </button>\n </div>\n {(formError || add.error || remove.error) && (\n <p className=\"text-2xs text-status-error\" role=\"alert\">\n {formError ?? (add.error as Error | null)?.message ?? (remove.error as Error | null)?.message}\n </p>\n )}\n </form>\n )}\n </div>\n );\n}\n","import { useMemo, useState, useCallback, useEffect } from 'react';\nimport { useNavigate, Link } from 'react-router-dom';\nimport { HelpCircle, Info, Tags, Layers, Braces, Sparkles, User, ListFilter, Search, AlertCircle, Boxes } from 'lucide-react';\nimport { SlidePanel, SlidePanelViews, PanelField, type SlidePanelView } from '../common/layout/SlidePanel';\nimport { MarkdownRenderer } from '../common/display/MarkdownRenderer';\nimport { JsonViewer } from '../common/data/JsonViewer';\nimport { StatusBadge } from '../common/display/StatusBadge';\nimport { EscalationTimeline } from '../common/display/EscalationTimeline';\nimport { RolePill } from '../common/display/RolePill';\nimport { CountdownTimer } from '../common/display/CountdownTimer';\nimport { DateValue } from '../common/display/DateValue';\nimport { CopyableId } from '../common/display/CopyableId';\nimport { UserName } from '../common/display/UserName';\nimport { TriageContext } from './TriageContext';\nimport { ErrorsPanel } from './ErrorsPanel';\nimport { buildHelpMarkdown, defaultHelpMarkdown } from '../../lib/x-lt-help';\nimport { deriveFieldLabel } from '../../lib/derive-field-label';\nimport { metadataFacetUrl } from '../../lib/facet-url';\nimport { displayMetadataEntries } from '../../lib/metadata-display';\nimport { escalationItems } from '../../lib/escalation-items';\nimport { EscalationItemsPanel } from './EscalationItemsPanel';\nimport type { LTEscalationRecord } from '../../api/types';\nimport type { FieldError } from '../../lib/field-validator';\n\nexport const ESCALATION_PANEL_VIEWS = {\n HELP: 'help',\n DETAILS: 'details',\n TRIAGE: 'triage',\n METADATA: 'metadata',\n ITEMS: 'items',\n CONTEXT: 'context',\n RECORD: 'record',\n ERRORS: 'errors',\n} as const;\n\nconst PANEL_VIEW_KEY = 'lt:escalation:panel:view';\n\nfunction readPanelView(validIds: readonly string[]): PanelViewId {\n try {\n const v = localStorage.getItem(PANEL_VIEW_KEY);\n if (v && validIds.includes(v)) return v as PanelViewId;\n } catch {}\n return ESCALATION_PANEL_VIEWS.HELP;\n}\n\ntype PanelViewId = (typeof ESCALATION_PANEL_VIEWS)[keyof typeof ESCALATION_PANEL_VIEWS];\n\nfunction MetadataList({ metadata, role }: { metadata: Record<string, unknown> | null; role: string }) {\n const entries = displayMetadataEntries(metadata);\n if (entries.length === 0) {\n return <p className=\"text-xs text-text-tertiary italic\">No metadata on this escalation.</p>;\n }\n return (\n <dl className=\"space-y-3.5\">\n {entries.map(([key, value]) => {\n const displayValue = typeof value === 'object' && value !== null\n ? JSON.stringify(value)\n : String(value);\n const roleUrl = metadataFacetUrl(key, value, role);\n const globalUrl = metadataFacetUrl(key, value);\n return (\n <div key={key} className=\"group relative\">\n <dt className=\"text-2xs font-medium text-text-secondary uppercase tracking-wide\">\n {deriveFieldLabel(key)}\n </dt>\n <dd className=\"mt-0.5 flex items-center gap-1\">\n <span className=\"text-xs text-text-primary font-mono break-all flex-1\">\n {displayValue}\n </span>\n <Link\n to={roleUrl}\n title={`Filter this queue: ${key} = ${displayValue}`}\n className=\"opacity-50 group-hover:opacity-100 transition-opacity shrink-0 p-0.5 text-text-quaternary hover:text-accent\"\n >\n <ListFilter className=\"w-3 h-3\" />\n </Link>\n <Link\n to={globalUrl}\n title={`Search all queues: ${key} = ${displayValue}`}\n className=\"opacity-50 group-hover:opacity-100 transition-opacity shrink-0 p-0.5 text-text-quaternary hover:text-accent\"\n >\n <Search className=\"w-3 h-3\" />\n </Link>\n </dd>\n </div>\n );\n })}\n </dl>\n );\n}\n\n/** The escalation's status facts — what used to sit as a strip above the form. */\nfunction DetailsList({ esc, claimed, isTerminal, isBuilder, traceUrl }: {\n esc: LTEscalationRecord;\n claimed: boolean;\n isTerminal: boolean;\n isBuilder: boolean;\n traceUrl: string | null;\n}) {\n return (\n <div className=\"space-y-5\">\n <dl className=\"space-y-3.5\">\n <PanelField label=\"Status\"><StatusBadge status={esc.status} /></PanelField>\n <PanelField label=\"Assigned to Role\"><RolePill role={esc.role} size=\"md\" /></PanelField>\n <PanelField label=\"Priority\">P{esc.priority}</PanelField>\n {(claimed || isTerminal) && esc.assigned_to && (\n <PanelField label=\"Claimed By\">\n <span className=\"inline-flex items-center gap-1.5 font-medium text-text-primary\">\n <User className=\"w-3 h-3 shrink-0 text-accent/75\" />\n <UserName userId={esc.assigned_to} />\n </span>\n </PanelField>\n )}\n <PanelField label=\"Created\"><DateValue date={esc.created_at} /></PanelField>\n {(claimed || isTerminal) && esc.claimed_at && (\n <PanelField label=\"Claimed\"><DateValue date={esc.claimed_at} /></PanelField>\n )}\n {claimed && !isTerminal && esc.assigned_until && (\n <PanelField label=\"Time Remaining\"><CountdownTimer until={esc.assigned_until} /></PanelField>\n )}\n {esc.resolved_at && (\n <PanelField label=\"Completed\">\n <span className=\"text-status-success\"><DateValue date={esc.resolved_at} /></span>\n </PanelField>\n )}\n </dl>\n\n {/* Identifiers — builder-facing references, quietly below a divider */}\n {isBuilder && (\n <>\n <hr className=\"border-surface-border/60\" />\n <div className=\"space-y-3.5\">\n <CopyableId label=\"Escalation ID\" value={esc.id} />\n {esc.task_id && (\n <CopyableId label=\"Task ID\" value={esc.task_id} href={`/workflows/tasks/detail/${esc.task_id}`} />\n )}\n {esc.workflow_type && (\n <CopyableId label=\"Workflow Name\" value={esc.workflow_type} href={`/workflows/executions?entity=${encodeURIComponent(esc.workflow_type)}`} />\n )}\n {esc.workflow_id && (\n <CopyableId label=\"Workflow ID\" value={esc.workflow_id} href={`/workflows/executions/${esc.workflow_id}`} />\n )}\n {esc.task_queue && (\n <CopyableId label=\"Task Queue\" value={esc.task_queue} href={`/admin/controlplane?queue=${encodeURIComponent(esc.task_queue)}`} />\n )}\n {esc.origin_id && esc.origin_id !== esc.workflow_id && (\n <CopyableId label=\"Origin\" value={esc.origin_id} href={`/processes/detail/${esc.origin_id}`} />\n )}\n {esc.trace_id && (\n <CopyableId\n label=\"Trace\"\n value={esc.trace_id}\n href={traceUrl ? traceUrl.replace('{traceId}', esc.trace_id) : undefined}\n external\n />\n )}\n </div>\n </>\n )}\n </div>\n );\n}\n\n\n/**\n * The escalation detail side panel — a set of views beside the resolve form,\n * ordered by specificity:\n *\n * Help — the form's `x-lt-help` markdown ({{token}}-interpolated), or a\n * state-aware hint when the schema carries none\n * Details — status, role, claim provenance, timestamps, identifiers\n * AI Analysis — what triage diagnosed and corrected (when AI is enabled and\n * the payload carries triage data)\n * Metadata — the row's metadata values, the facts stamped at enqueue\n * Items — the held items of an accumulator or batch row, with add and\n * remove on a pending accumulator\n * Context — the expanded surface: input envelope, escalation context, and\n * resolver payload\n * Record — the raw escalation record (builders only)\n */\nexport function EscalationSidePanel({\n esc,\n schema,\n envelope,\n payload,\n resolver,\n triage,\n hasAI,\n claimed,\n claimedByMe,\n isTerminal,\n isBuilder,\n traceUrl,\n open,\n noGutter,\n formErrors,\n activePanel,\n onPanelChange,\n canWriteItems,\n}: {\n esc: LTEscalationRecord;\n schema: Record<string, unknown> | null;\n envelope: Record<string, unknown> | null;\n payload: Record<string, unknown> | null;\n resolver: Record<string, unknown> | null;\n triage: Record<string, unknown> | null;\n hasAI: boolean;\n claimed: boolean;\n claimedByMe: boolean;\n isTerminal: boolean;\n isBuilder: boolean;\n traceUrl: string | null;\n open: boolean;\n /** Remove the left gutter so the panel background is flush with the content column (used in full-bleed iframe mode). */\n noGutter?: boolean;\n /** Structured form validation errors from the last submit attempt. When non-empty, the errors panel is available. */\n formErrors?: FieldError[];\n /** Controlled active panel view id. When provided, the panel uses this instead of internal state. */\n activePanel?: string;\n /** Called when the user changes the active panel view. */\n onPanelChange?: (id: string) => void;\n /** The actor may add and remove items on a pending accumulator row. */\n canWriteItems?: boolean;\n}) {\n const navigate = useNavigate();\n const ALL_VIEW_IDS = Object.values(ESCALATION_PANEL_VIEWS) as readonly string[];\n const [internalView, setInternalView] = useState<PanelViewId>(() => readPanelView(ALL_VIEW_IDS));\n\n const activeView = (activePanel as PanelViewId | undefined) ?? internalView;\n\n const handleViewChange = useCallback((id: string) => {\n try { localStorage.setItem(PANEL_VIEW_KEY, id); } catch {}\n setInternalView(id as PanelViewId);\n onPanelChange?.(id);\n }, [onPanelChange]);\n\n // Sync to controlled activePanel when it changes externally (e.g. page forces 'errors' after submit).\n useEffect(() => {\n if (activePanel && activePanel !== internalView) {\n setInternalView(activePanel as PanelViewId);\n }\n }, [activePanel]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const helpMarkdown = useMemo(() => {\n const authored = buildHelpMarkdown(schema, {\n escalation: esc as unknown as Record<string, unknown>,\n metadata: esc.metadata ?? null,\n envelope,\n payload,\n resolver,\n });\n return authored ?? defaultHelpMarkdown({ isTerminal, status: esc.status, claimed, claimedByMe });\n }, [schema, esc, envelope, payload, resolver, isTerminal, claimed, claimedByMe]);\n\n // Relative links in help markdown navigate in-app instead of reloading.\n const handleHelpClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n const anchor = (e.target as HTMLElement).closest('a');\n const href = anchor?.getAttribute('href');\n if (href?.startsWith('/')) {\n e.preventDefault();\n navigate(href);\n }\n }, [navigate]);\n\n const items = useMemo(() => escalationItems(esc, envelope), [esc, envelope]);\n\n const views: SlidePanelView[] = [\n {\n id: ESCALATION_PANEL_VIEWS.HELP,\n icon: HelpCircle,\n label: 'Instructions',\n content: <MarkdownRenderer content={helpMarkdown} onClick={handleHelpClick} />,\n },\n {\n id: ESCALATION_PANEL_VIEWS.DETAILS,\n icon: Info,\n label: 'Details',\n content: <DetailsList esc={esc} claimed={claimed} isTerminal={isTerminal} isBuilder={isBuilder} traceUrl={traceUrl} />,\n },\n ...(hasAI && triage\n ? [{\n id: ESCALATION_PANEL_VIEWS.TRIAGE,\n icon: Sparkles,\n label: 'AI Analysis',\n content: <TriageContext triage={triage} payload={payload ?? {}} />,\n }]\n : []),\n {\n id: ESCALATION_PANEL_VIEWS.METADATA,\n icon: Tags,\n label: 'Metadata',\n content: <MetadataList metadata={esc.metadata} role={esc.role} />,\n },\n ...(items\n ? [{\n id: ESCALATION_PANEL_VIEWS.ITEMS,\n icon: Boxes,\n label: 'Items',\n content: (\n <EscalationItemsPanel\n escalationId={esc.id}\n items={items}\n canWrite={!!canWriteItems && esc.status === 'pending'}\n />\n ),\n }]\n : []),\n {\n id: ESCALATION_PANEL_VIEWS.CONTEXT,\n icon: Layers,\n label: 'Context',\n content: (\n <div className=\"space-y-5\">\n {envelope != null && <JsonViewer data={envelope} label=\"Input Envelope\" />}\n {payload != null && <JsonViewer data={payload} label=\"Escalation Context\" />}\n {resolver != null && <JsonViewer data={resolver} label=\"Resolver Payload\" />}\n {envelope == null && payload == null && resolver == null && (\n <p className=\"text-xs text-text-tertiary italic\">No context on this escalation.</p>\n )}\n </div>\n ),\n },\n ...(isBuilder\n ? [{\n id: ESCALATION_PANEL_VIEWS.RECORD,\n icon: Braces,\n label: 'Record',\n content: <JsonViewer data={esc} label=\"Escalation Record\" />,\n }]\n : []),\n ...(formErrors && formErrors.length > 0\n ? [{\n id: ESCALATION_PANEL_VIEWS.ERRORS,\n icon: (({ className }: { className?: string }) => (\n <AlertCircle className={`${className ?? ''} text-status-error`} />\n )) as SlidePanelView['icon'],\n label: 'Errors',\n content: <ErrorsPanel errors={formErrors} schema={schema} />,\n }]\n : []),\n ];\n\n return (\n <SlidePanel open={open} width={384} className=\"h-full\">\n {/* The breathing room collapses with the panel — it lives inside the\n animated width, so the closed state reclaims all of it. The column\n is fixed height (like the left nav): the sparkline row persists at\n the top and the active view scrolls independently beneath it. */}\n <div className={`h-full${noGutter ? '' : ' pl-6'}`}>\n {/* One tinted column, top to bottom — the sparkline lives inside it,\n hovering tight (2px y padding) above the icon row, centered at 80%\n so its edge labels never clip. The slim row drops the icons onto\n the same visual line as the page-title icons beside them; only the\n view content below scrolls. */}\n <div className=\"h-full flex flex-col bg-surface-hover\">\n <div className=\"shrink-0 pt-2 pb-0 flex justify-center\">\n <EscalationTimeline esc={esc} className=\"w-[80%]\" />\n </div>\n <div className=\"flex-1 min-h-0\">\n {/* No close button — the page-header panel icon owns dismissal. */}\n <SlidePanelViews\n views={views}\n activeId={activeView}\n onViewChange={handleViewChange}\n stickyClassName=\"h-full min-h-0\"\n />\n </div>\n </div>\n </div>\n </SlidePanel>\n );\n}\n","import { useState, useCallback } from 'react';\nimport { IdCard } from 'lucide-react';\nimport { buildResolverPayload } from '../../../lib/resolver-payload';\nimport { CountdownTimer } from '../../../components/common/display/CountdownTimer';\nimport { UserName } from '../../../components/common/display/UserName';\nimport { CustomDurationPicker } from '../../../components/common/form/CustomDurationPicker';\nimport { useClaimDurations } from '../../../hooks/useClaimDurations';\nimport type { EscalationActionBarProps } from './action-bar-types';\n\nexport type { ActionBarMode, ActiveView, EscalationActionBarProps } from './action-bar-types';\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport function EscalationActionBar(props: EscalationActionBarProps) {\n const {\n mode, activeView, onActiveViewChange,\n onClaim, claimPending, claimNudge,\n workflowType, json, onResolve, resolvePending, resolveError,\n requestTriage, triageNotes,\n onRelease, releasePending,\n onCancel,\n assignedTo, assignedUntil,\n onSubmitAttempt,\n onValidationErrors,\n escalationContext,\n labels = {},\n submitBlocked,\n submitBlockedMessage,\n actingName,\n submitNeedsBadge,\n canManage,\n onReassign,\n onUnassign,\n unassignPending,\n } = props;\n\n const claimDurations = useClaimDurations();\n const [duration, setDuration] = useState('30');\n const [customMinutes, setCustomMinutes] = useState(0);\n const [parseError, setParseError] = useState('');\n\n const isCustom = duration === 'custom';\n const onCustomChange = useCallback((m: number) => setCustomMinutes(m), []);\n\n // x-lt-submit-guard is owned by the page (it drives auto-resolve too); the bar\n // just reflects its block state. The gate applies to the form resolve only —\n // triage stays the escape hatch when the guarded work itself is the problem.\n const guardBlocksSubmit = !!submitBlocked && !requestTriage;\n\n if (mode === 'terminal') return null;\n\n const handleClaim = () => {\n const minutes = isCustom ? customMinutes : parseInt(duration);\n if (!minutes || minutes <= 0) return;\n onClaim(minutes);\n };\n\n const handleSubmitResolve = () => {\n setParseError('');\n\n // When triage is requested, ignore the form payload entirely.\n // Only the triage flag and notes matter — the form data (e.g.\n // approved: true) must NOT leak through, as it would confuse\n // the triage workflow into thinking the issue is already resolved.\n if (requestTriage) {\n const payload: Record<string, unknown> = {\n _lt: { needsTriage: true },\n };\n if (triageNotes.trim()) payload.notes = triageNotes.trim();\n onResolve(payload);\n return;\n }\n\n // Parse, validate against the embedded _form_schema, and map to the final\n // nested shape — the same pass the API layer runs on enforced roles, so the\n // panel and a server 422 report the identical list. Hidden fields (x-lt-showIf\n // falsy against the live context) are excluded from the required checks.\n const result = buildResolverPayload(json, escalationContext);\n if (result.parseError) {\n setParseError(result.parseError);\n return;\n }\n if (result.errors.length > 0) {\n // Field errors live in ONE surface: the errors panel, which opens on the\n // blocked submit and recomputes as the user fixes fields. A summary here\n // would go stale the moment a field is corrected — the footer keeps only\n // its own errors (bad JSON, server rejection).\n onSubmitAttempt?.();\n onValidationErrors?.(result.errors);\n return;\n }\n onResolve(result.payload!);\n };\n\n const tabClass = (active: boolean) =>\n `text-xs transition-colors ${active ? 'text-accent font-medium' : 'text-text-tertiary hover:text-accent'}`;\n\n return (\n // Flex-pinned at the bottom of the form column: full-bleed left into the\n // page gutter, ending at the side panel's edge on the right.\n <div className=\"shrink-0 h-[80px] bg-surface/95 backdrop-blur-sm border-t border-surface-border -ml-10 pl-10 pr-10 pt-3 pb-5\" data-testid=\"escalation-action-bar\">\n\n {/* ── Available: claim ── */}\n {mode === 'available' && (\n <div data-testid=\"claim-bar\">\n {/* Duration tab row */}\n <div className=\"flex items-center gap-4 mb-2\">\n {claimDurations.map((opt) => (\n <button\n key={opt.value}\n onClick={() => { setDuration(opt.value); setCustomMinutes(0); }}\n className={tabClass(!isCustom && duration === opt.value)}\n >\n {opt.label}\n </button>\n ))}\n <button\n onClick={() => setDuration('custom')}\n className={tabClass(isCustom)}\n >\n Other\n </button>\n </div>\n {/* Action row — right-aligned */}\n <div className=\"flex items-center gap-3\">\n {labels.cancel !== false && (\n <button\n onClick={onCancel}\n className=\"text-xs text-text-tertiary hover:text-status-error transition-colors\"\n >\n {labels.cancel ?? 'Cancel escalation'}\n </button>\n )}\n <div className=\"flex-1\" />\n {isCustom && (\n <CustomDurationPicker onChange={onCustomChange} compact autoFocus />\n )}\n <button\n // Keyed by the nudge count: each locked-form click remounts\n // the button, restarting the wiggle from the top.\n key={claimNudge}\n onClick={handleClaim}\n disabled={claimPending || (isCustom && customMinutes <= 0)}\n className={`btn-primary text-xs ${claimNudge ? 'animate-[field-shake_0.4s_ease-in-out]' : ''}`}\n data-testid=\"claim-button\"\n >\n {claimPending ? 'Claiming...' : (labels.claim ?? 'Claim')}\n </button>\n </div>\n </div>\n )}\n\n {/* ── Claimed by other ── */}\n {mode === 'claimed_by_other' && (\n <div className=\"flex items-center gap-4\" data-testid=\"claimed-other-bar\">\n <p className=\"text-sm text-text-secondary\">\n Claimed by <span className=\"font-medium text-text-primary\">{assignedTo ? <UserName userId={assignedTo} /> : 'unknown'}</span>\n </p>\n {assignedUntil && <CountdownTimer until={assignedUntil} />}\n {resolveError && <span className=\"text-xs text-status-error\">{resolveError.message}</span>}\n {canManage && (\n <>\n <div className=\"flex-1\" />\n <button\n onClick={onReassign}\n className=\"text-xs text-text-tertiary hover:text-accent transition-colors\"\n data-testid=\"admin-reassign\"\n >\n Reassign…\n </button>\n <button\n onClick={onUnassign}\n disabled={unassignPending}\n className=\"text-xs text-text-tertiary hover:text-status-error transition-colors disabled:opacity-50\"\n data-testid=\"admin-unassign\"\n >\n {unassignPending ? 'Returning…' : 'Return to queue'}\n </button>\n </>\n )}\n </div>\n )}\n\n {/* ── Claimed by me ── */}\n {mode === 'claimed_by_me' && (\n <div data-testid=\"action-bar\">\n {/* Tab row */}\n <div className=\"flex items-center gap-4 mb-2\">\n <button\n onClick={() => onActiveViewChange('resolve')}\n className={tabClass(activeView === 'resolve')}\n >\n {workflowType ? 'Resolve' : 'Acknowledge'}\n </button>\n <button\n onClick={() => onActiveViewChange('release')}\n className={`text-xs transition-colors ${activeView === 'release' ? 'text-status-error font-medium' : 'text-text-tertiary hover:text-status-error'}`}\n >\n {labels.release ?? 'Release'}\n </button>\n {labels.cancel !== false && (\n <button\n onClick={onCancel}\n className=\"text-xs transition-colors text-text-tertiary hover:text-status-error\"\n >\n {labels.cancel ?? 'Cancel'}\n </button>\n )}\n </div>\n\n {/* ── Resolve controls ── */}\n {activeView === 'resolve' && (\n <div className=\"flex items-center gap-4\">\n {submitNeedsBadge ? (\n <p className=\"flex items-center gap-1.5 text-sm text-text-secondary\" data-testid=\"submit-badge-warning\">\n <IdCard className=\"w-4 h-4 text-accent-muted shrink-0\" strokeWidth={1.5} />\n When you submit, you'll scan your badge to confirm you're{' '}\n <span className=\"font-medium text-text-primary\">\n {assignedTo ? <UserName userId={assignedTo} /> : 'the claimant'}\n </span>.\n </p>\n ) : actingName && (\n <p className=\"text-sm text-text-secondary\" data-testid=\"acting-claim-note\">\n Claimed by you <span className=\"font-medium text-text-primary\">({actingName})</span>\n </p>\n )}\n {!submitNeedsBadge && actingName && assignedUntil && <CountdownTimer until={assignedUntil} />}\n <div className=\"flex-1\" />\n {parseError && <span className=\"text-xs text-status-error\">{parseError}</span>}\n {resolveError && <span className=\"text-xs text-status-error\">{resolveError.message}</span>}\n {guardBlocksSubmit && submitBlockedMessage && (\n <span className=\"text-xs text-status-pending\" data-testid=\"submit-guard-message\">\n {submitBlockedMessage}\n </span>\n )}\n <button\n onClick={handleSubmitResolve}\n disabled={resolvePending || guardBlocksSubmit}\n className=\"btn-primary text-xs\"\n >\n {resolvePending\n ? (workflowType ? 'Submitting...' : 'Acknowledging...')\n : requestTriage ? 'Send to Triage'\n : (labels.submit ?? (workflowType ? 'Submit' : 'Acknowledge'))}\n </button>\n </div>\n )}\n\n {/* ── Release controls ── */}\n {activeView === 'release' && (\n <div className=\"flex items-center gap-3\">\n <span className=\"text-xs text-text-secondary\">Release back to pool?</span>\n <div className=\"flex-1\" />\n <button onClick={() => onActiveViewChange('resolve')} className=\"btn-secondary text-xs\">\n Back\n </button>\n <button\n onClick={onRelease}\n disabled={releasePending}\n className=\"btn-primary text-xs bg-status-error hover:bg-status-error/80\"\n >\n {releasePending ? 'Releasing...' : 'Yes, Release'}\n </button>\n </div>\n )}\n\n </div>\n )}\n </div>\n );\n}\n","import { useState } from 'react';\nimport { Link } from 'react-router-dom';\n\nimport { JsonViewer } from '../common/data/JsonViewer';\nimport { SimpleMarkdown } from '../common/display/SimpleMarkdown';\n\ninterface RoundsExhaustedContextProps {\n payload: Record<string, unknown>;\n isTerminal?: boolean;\n resolverPayload?: Record<string, unknown> | null;\n onRetryTriage?: () => void;\n isRetrying?: boolean;\n}\n\nfunction SectionLabel({ children }: { children: React.ReactNode }) {\n return (\n <p className=\"text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-1.5\">{children}</p>\n );\n}\n\nexport function RoundsExhaustedContext({ payload, isTerminal, resolverPayload, onRetryTriage, isRetrying }: RoundsExhaustedContextProps) {\n const title = payload.title as string | undefined;\n const summary = payload.summary as string | undefined;\n const diagnosis = payload.diagnosis as string | undefined;\n const toolCalls = payload.tool_calls_made as number | undefined;\n const result = payload.result as Record<string, unknown> | null | undefined;\n\n // Detect triage resolution\n const ltMeta = resolverPayload?._lt as Record<string, unknown> | undefined;\n const wasTriaged = ltMeta?.triaged === true;\n const triageWorkflowId = ltMeta?.triageWorkflowId as string | undefined;\n const isResolvedByTriage = isTerminal && wasTriaged;\n\n const [showResult, setShowResult] = useState(false);\n\n // Resolved by triage — show success state\n if (isResolvedByTriage) {\n return (\n <div className=\"rounded-md border border-status-success/30 bg-surface-raised\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-surface-border\">\n <div className=\"flex items-center gap-2\">\n <svg className=\"w-4 h-4 text-status-success shrink-0\" viewBox=\"0 0 16 16\" fill=\"currentColor\">\n <path d=\"M8 1a7 7 0 110 14A7 7 0 018 1zm3.36 4.65a.5.5 0 00-.72 0L7 9.29 5.36 7.65a.5.5 0 10-.72.7l2 2a.5.5 0 00.72 0l4-4a.5.5 0 000-.7z\" />\n </svg>\n <span className=\"text-xs font-medium text-status-success\">Resolved by AI Triage</span>\n </div>\n {typeof toolCalls === 'number' && (\n <span className=\"text-2xs font-mono px-2 py-0.5 rounded-full bg-status-success/10 text-status-success\">\n {toolCalls} tool calls used\n </span>\n )}\n </div>\n\n {/* Body */}\n <div className=\"px-4 py-4 space-y-4\">\n {title && (\n <p className=\"text-sm font-medium text-text-primary\">{title}</p>\n )}\n\n {diagnosis && (\n <div>\n <SectionLabel>Triage diagnosis</SectionLabel>\n <div className=\"text-xs text-text-primary leading-relaxed\">\n <SimpleMarkdown content={diagnosis} />\n </div>\n </div>\n )}\n\n <div className=\"flex items-center gap-3\">\n {triageWorkflowId && (\n <Link\n to={`/workflows/executions/${triageWorkflowId}`}\n className=\"text-2xs text-accent hover:underline\"\n >\n View triage execution\n </Link>\n )}\n </div>\n </div>\n </div>\n );\n }\n\n // Default: warning state\n return (\n <div className=\"rounded-md border border-status-warning/30 bg-surface-raised\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-surface-border\">\n <div className=\"flex items-center gap-2\">\n <svg className=\"w-4 h-4 text-status-warning shrink-0\" viewBox=\"0 0 16 16\" fill=\"currentColor\">\n <path d=\"M8 1a7 7 0 110 14A7 7 0 018 1zm0 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 4zm0 7a.75.75 0 100-1.5.75.75 0 000 1.5z\" />\n </svg>\n <span className=\"text-xs font-medium text-status-warning\">Tool Rounds Exhausted</span>\n </div>\n {typeof toolCalls === 'number' && (\n <span className=\"text-2xs font-mono px-2 py-0.5 rounded-full bg-status-warning/10 text-status-warning\">\n {toolCalls} tool calls used\n </span>\n )}\n </div>\n\n {/* Body */}\n <div className=\"px-4 py-4 space-y-4\">\n {title && (\n <p className=\"text-sm font-medium text-text-primary\">{title}</p>\n )}\n\n {summary && (\n <div>\n <SectionLabel>What happened</SectionLabel>\n <div className=\"text-xs text-text-secondary leading-relaxed\">\n <SimpleMarkdown content={summary} />\n </div>\n </div>\n )}\n\n {diagnosis && (\n <div>\n <SectionLabel>What went wrong</SectionLabel>\n <div className=\"text-xs text-text-primary leading-relaxed\">\n <SimpleMarkdown content={diagnosis} />\n </div>\n </div>\n )}\n\n {result != null && (\n <div>\n <button\n onClick={() => setShowResult(!showResult)}\n className=\"flex items-center gap-1 text-2xs font-semibold uppercase tracking-widest text-text-tertiary hover:text-text-secondary transition-colors\"\n >\n <svg className={`w-3 h-3 transition-transform ${showResult ? 'rotate-90' : ''}`} viewBox=\"0 0 12 12\" fill=\"currentColor\">\n <path d=\"M4.5 2l4 4-4 4\" />\n </svg>\n Partial results\n </button>\n {showResult && (\n <div className=\"mt-2\">\n <JsonViewer data={result} defaultMode=\"tree\" />\n </div>\n )}\n </div>\n )}\n\n {onRetryTriage && !isTerminal && (\n <div className=\"pt-2\">\n <button\n onClick={onRetryTriage}\n disabled={isRetrying}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-xs font-medium bg-accent text-text-inverse rounded-md hover:bg-accent/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n <svg className=\"w-3.5 h-3.5\" viewBox=\"0 0 14 14\" fill=\"currentColor\">\n <path d=\"M7 1a6 6 0 100 12A6 6 0 007 1zm2.5 6.5l-3.5 2V5.5l3.5 2z\" />\n </svg>\n {isRetrying ? 'Sending to triage...' : 'Retry with AI Triage'}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n}\n","import type { LTEscalationRecord } from '../api/types';\n\nexport const VIEWPORT_STAGES = {\n /** Pending and not workable by the viewer: available, or claimed by someone else. */\n PENDING: 'pending',\n /** Pending and workable by the viewer: the editor stage. */\n CLAIMED: 'claimed',\n RESOLVED: 'resolved',\n} as const;\nexport type ViewportStage = (typeof VIEWPORT_STAGES)[keyof typeof VIEWPORT_STAGES];\n\n/** The `x-lt-viewport` declaration: `src` is the claimed (editor) URL. */\nexport interface XLtViewport {\n type: 'iframe';\n src: string;\n onPending?: string;\n onResolved?: string;\n}\n\nexport function readViewport(schema: Record<string, unknown> | null | undefined): XLtViewport | null {\n const vp = schema?.['x-lt-viewport'] as Partial<XLtViewport> | undefined;\n if (vp?.type !== 'iframe' || typeof vp.src !== 'string' || !vp.src) return null;\n return vp as XLtViewport;\n}\n\n/** The stage an escalation is in for the viewer; null for cancelled rows. */\nexport function viewportStage(esc: LTEscalationRecord, editable: boolean): ViewportStage | null {\n if (esc.status === 'resolved') return VIEWPORT_STAGES.RESOLVED;\n if (esc.status !== 'pending') return null;\n return editable ? VIEWPORT_STAGES.CLAIMED : VIEWPORT_STAGES.PENDING;\n}\n\n/** The declared URL for a stage, or null when the declaration leaves that stage to the default surface. */\nexport function viewportSrcForStage(viewport: XLtViewport, stage: ViewportStage): string | null {\n if (stage === VIEWPORT_STAGES.CLAIMED) return viewport.src;\n if (stage === VIEWPORT_STAGES.PENDING) return viewport.onPending ?? null;\n return viewport.onResolved ?? null;\n}\n\n/**\n * Expands `{key}` tokens in a viewport URL from the escalation's sources,\n * merged so resolver_payload wins over escalation_payload, which wins over\n * envelope, which wins over metadata. Unmatched tokens stay in place.\n */\nexport function expandViewportSrc(src: string, esc: LTEscalationRecord): string {\n if (!src.includes('{')) return src;\n try {\n const parse = (s: string | null | undefined): Record<string, unknown> => {\n if (!s) return {};\n try { return JSON.parse(s) as Record<string, unknown>; } catch { return {}; }\n };\n const merged = {\n ...(esc.metadata ?? {}),\n ...parse(esc.envelope),\n ...parse(esc.escalation_payload),\n ...parse(esc.resolver_payload),\n };\n return src.replace(/\\{([^}]+)\\}/g, (_, key) =>\n Object.prototype.hasOwnProperty.call(merged, key) ? String(merged[key]) : `{${key}}`\n );\n } catch {\n return src;\n }\n}\n","import { useCallback, useEffect, useRef } from 'react';\nimport type { LTEscalationRecord } from '../../api/types';\nimport { VIEWPORT_STAGES, type ViewportStage } from '../../lib/x-lt-viewport';\n\n// ---------------------------------------------------------------------------\n// postMessage protocol types\n// ---------------------------------------------------------------------------\n\n/** Messages sent from the parent to the iframe. */\ntype ParentMessage =\n | { type: 'lt:init'; escalation: IframeEscalationData; schema: Record<string, unknown>; stage: ViewportStage }\n | { type: 'lt:requestSubmit' }\n | { type: 'lt:validate' };\n\n/** Messages received from the iframe. */\ntype ChildMessage =\n | { type: 'lt:ready' }\n | { type: 'lt:submit'; payload: Record<string, unknown> }\n | { type: 'lt:escalate'; target: string }\n | { type: 'lt:resize'; height: number };\n\n/**\n * Escalation context delivered to the iframe via `lt:init`. Includes the\n * full envelope and metadata so the embedded app has all the context it\n * needs to load the right session and submit a meaningful payload back.\n *\n * The iframe is origin-validated before any message is sent, so secrets in\n * the envelope only travel to the declared src origin.\n */\ninterface IframeEscalationData {\n id: string;\n type: string;\n subtype: string;\n description: string | null;\n status: string;\n priority: number;\n role: string;\n workflow_type: string | null;\n envelope: Record<string, unknown> | null;\n metadata: Record<string, unknown> | null;\n escalation_payload: Record<string, unknown> | null;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\ninterface IframeViewportProps {\n src: string;\n escalation: LTEscalationRecord;\n schema: Record<string, unknown>;\n /** The stage the URL was chosen for; travels on `lt:init`. */\n stage?: ViewportStage;\n onResolve?: (payload: Record<string, unknown>) => void;\n onEscalate?: (targetRole: string) => void;\n /** Pending and resolved stages: the embed shows, never submits. */\n readOnly?: boolean;\n submitAttempted?: boolean;\n /** Fill the parent container edge-to-edge (absolute inset-0). Used for full-bleed iframe mode. */\n fill?: boolean;\n}\n\nfunction safeParse(s: string | null | undefined): Record<string, unknown> | null {\n if (!s) return null;\n try { return JSON.parse(s) as Record<string, unknown>; } catch { return null; }\n}\n\n/**\n * Renders a sandboxed iframe for fully custom HITL UIs.\n *\n * Protocol — parent → iframe:\n * `lt:init` escalation context (id, envelope, metadata, payload) + schema\n * `lt:requestSubmit` ask the iframe to trigger its own submit flow\n * `lt:validate` ask the iframe to report validation errors\n *\n * Protocol — iframe → parent:\n * `lt:ready` iframe loaded, ready to receive `lt:init`\n * `lt:submit` work complete; payload becomes the resolver payload\n * `lt:escalate` re-route to a different role\n * `lt:resize` resize the iframe height\n *\n * The iframe can detect it is embedded via `window !== window.top` and\n * opt in to postMessage communication instead of its own submit UX.\n */\nexport function IframeViewport({ src, escalation, schema, stage = VIEWPORT_STAGES.CLAIMED, onResolve, onEscalate, readOnly = false, submitAttempted, fill }: IframeViewportProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const iframeOrigin = useRef<string>('');\n\n // Parse the allowed origin from the src URL\n useEffect(() => {\n try {\n const url = new URL(src);\n iframeOrigin.current = url.origin;\n } catch {\n iframeOrigin.current = '';\n }\n }, [src]);\n\n const sendInit = useCallback(() => {\n const iframe = iframeRef.current;\n if (!iframe?.contentWindow || !iframeOrigin.current) return;\n\n const data: IframeEscalationData = {\n id: escalation.id,\n type: escalation.type,\n subtype: escalation.subtype,\n description: escalation.description,\n status: escalation.status,\n priority: escalation.priority,\n role: escalation.role,\n workflow_type: escalation.workflow_type,\n envelope: safeParse(escalation.envelope),\n metadata: escalation.metadata ?? null,\n escalation_payload: safeParse(escalation.escalation_payload),\n };\n\n const message: ParentMessage = { type: 'lt:init', escalation: data, schema, stage };\n iframe.contentWindow.postMessage(message, iframeOrigin.current);\n }, [escalation, schema, stage]);\n\n // Listen for messages from the iframe\n useEffect(() => {\n const handler = (event: MessageEvent) => {\n if (event.origin !== iframeOrigin.current) return;\n\n const data = event.data as ChildMessage;\n if (!data || typeof data !== 'object' || typeof data.type !== 'string') return;\n\n switch (data.type) {\n case 'lt:ready':\n sendInit();\n break;\n case 'lt:submit':\n if (!readOnly && data.payload && typeof data.payload === 'object') {\n onResolve?.(data.payload);\n }\n break;\n case 'lt:escalate':\n if (!readOnly && typeof data.target === 'string' && data.target) {\n onEscalate?.(data.target);\n }\n break;\n case 'lt:resize': {\n const iframe = iframeRef.current;\n if (iframe && typeof data.height === 'number' && data.height > 0) {\n iframe.style.height = `${Math.min(data.height, 2000)}px`;\n }\n break;\n }\n }\n };\n\n window.addEventListener('message', handler);\n return () => window.removeEventListener('message', handler);\n }, [sendInit, onResolve, onEscalate, readOnly]);\n\n // Forward lt:validate to iframe when the action bar triggers a submit attempt\n useEffect(() => {\n if (!submitAttempted || readOnly) return;\n const iframe = iframeRef.current;\n if (iframe?.contentWindow && iframeOrigin.current) {\n iframe.contentWindow.postMessage({ type: 'lt:validate' } as ParentMessage, iframeOrigin.current);\n }\n }, [submitAttempted, readOnly]);\n\n if (fill) {\n return (\n <div className=\"absolute inset-0 overflow-hidden\">\n <iframe\n ref={iframeRef}\n src={src}\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-pointer-lock allow-downloads\"\n className=\"w-full h-full border-0 block\"\n title=\"HITL Viewport\"\n onLoad={sendInit}\n />\n </div>\n );\n }\n\n return (\n // bg-white stays literal: customer HTML iframes assume a white canvas.\n <div className=\"rounded-md border border-surface-border overflow-hidden bg-white\">\n <iframe\n ref={iframeRef}\n src={src}\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-pointer-lock allow-downloads\"\n className=\"w-full border-0\"\n style={{ minHeight: '600px' }}\n title=\"HITL Viewport\"\n onLoad={sendInit}\n />\n </div>\n );\n}\n","import { ResolverForm } from '../../../components/escalation/ResolverForm';\nimport type { ShowIfContext } from '../../../lib/x-lt-show-if';\n\nexport function ResolverSection({\n json,\n onJsonChange,\n requestTriage,\n onRequestTriageChange,\n triageNotes,\n onTriageNotesChange,\n disabled = false,\n submitAttempted = false,\n showTriage = false,\n escalationContext,\n onOpenHelp,\n onDisabledClick,\n disabledHint,\n}: {\n json: string;\n onJsonChange: (v: string) => void;\n requestTriage: boolean;\n onRequestTriageChange: (v: boolean) => void;\n triageNotes: string;\n onTriageNotesChange: (v: string) => void;\n disabled?: boolean;\n submitAttempted?: boolean;\n showTriage?: boolean;\n escalationContext?: ShowIfContext;\n /** Opens the Instructions side-panel view (help icon beside the form title). */\n onOpenHelp?: () => void;\n /** Fires when the user clicks the locked (unclaimed) form. */\n onDisabledClick?: () => void;\n /** Cursor-following hint over the locked form. */\n disabledHint?: React.ReactNode;\n}) {\n return (\n <>\n {/* Triage callout */}\n {showTriage && (\n <div className=\"flex items-center justify-between mb-3\">\n {requestTriage ? (\n <button\n onClick={() => onRequestTriageChange(false)}\n className=\"group flex items-center gap-2 text-left\"\n data-testid=\"triage-cancel\"\n >\n <span className=\"flex items-center justify-center w-5 h-5 rounded-full bg-accent/10 text-accent shrink-0\">\n <svg className=\"w-3 h-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </span>\n <span className=\"text-xs text-text-secondary group-hover:text-text-primary transition-colors\">\n AI Triage active.{' '}\n <span className=\"text-accent group-hover:underline\">Cancel</span>\n </span>\n </button>\n ) : (\n <button\n onClick={() => onRequestTriageChange(true)}\n className=\"group flex items-center gap-2.5 text-left my-4\"\n data-testid=\"triage-callout\"\n >\n <span className=\"flex items-center justify-center w-6 h-6 rounded-full bg-accent/10 text-accent shrink-0 animate-[triage-glow_6s_ease-in-out_infinite]\">\n <svg className=\"w-3.5 h-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9.663 17h4.674M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z\" />\n </svg>\n </span>\n <span className=\"text-sm text-text-secondary group-hover:text-text-primary transition-colors\">\n This form doesn't capture the issue?{' '}\n <span className=\"text-accent group-hover:underline\">Request AI Triage</span>\n </span>\n </button>\n )}\n </div>\n )}\n\n <div className=\"relative min-h-[200px]\">\n {/* Form controls */}\n <div className={requestTriage ? 'pointer-events-none select-none' : ''}>\n <ResolverForm value={json} onChange={onJsonChange} disabled={disabled} submitAttempted={submitAttempted} escalationContext={escalationContext} onOpenHelp={onOpenHelp} onDisabledClick={onDisabledClick} disabledHint={disabledHint} />\n </div>\n\n {/* Triage overlay — occludes the form when AI Triage is checked */}\n {requestTriage && (\n <div\n className=\"absolute inset-0 z-10 flex flex-col bg-surface/90 backdrop-blur-[2px] rounded-md border border-accent/20 p-5\"\n data-testid=\"triage-overlay\"\n >\n <p className=\"text-xs text-text-secondary mb-3 leading-relaxed\">\n The resolution form will not be submitted. Describe the issue\n so AI triage can diagnose and fix it using available tools.\n The corrected result will come back as a new escalation for\n your review.\n </p>\n <textarea\n value={triageNotes}\n onChange={(e) => onTriageNotesChange(e.target.value)}\n placeholder=\"e.g. Content is in Spanish — needs translation to English before review...\"\n className=\"input text-xs w-full flex-1 min-h-[80px] resize-none\"\n autoFocus\n data-testid=\"triage-notes\"\n />\n </div>\n )}\n </div>\n </>\n );\n}\n","import { Link } from 'react-router-dom';\nimport { KeyRound, MonitorPlay } from 'lucide-react';\nimport { RoundsExhaustedContext } from '../../../components/escalation/RoundsExhaustedContext';\nimport { IframeViewport } from '../../../components/escalation/IframeViewport';\nimport { ResolverForm } from '../../../components/escalation/ResolverForm';\nimport { mapPayloadToForm } from '../../../lib/x-lt-bind';\nimport type { ShowIfContext } from '../../../lib/x-lt-show-if';\nimport { ResolverSection } from './ResolverSection';\nimport type { ActiveView } from './EscalationActionBar';\nimport type { LTEscalationRecord } from '../../../api/types';\nimport { VIEWPORT_STAGES, expandViewportSrc, readViewport, viewportSrcForStage } from '../../../lib/x-lt-viewport';\n\nexport { expandViewportSrc };\n\nfunction parseJson(s: string | null | undefined): Record<string, unknown> | null {\n if (!s) return null;\n try { return JSON.parse(s) as Record<string, unknown>; } catch { return null; }\n}\n\nexport function buildShowIfContext(\n esc: LTEscalationRecord,\n lookup?: Record<string, unknown> | null,\n): ShowIfContext {\n return {\n escalation: esc as unknown as Record<string, unknown>,\n metadata: esc.metadata ?? null,\n envelope: parseJson(esc.envelope),\n payload: parseJson(esc.escalation_payload),\n resolver: null,\n ...(lookup ? { lookup } : {}),\n };\n}\n\ninterface EscalationContextProps {\n isRoundsExhausted: boolean;\n payloadObj: Record<string, unknown> | null;\n isTerminal: boolean;\n resolverPayload: Record<string, unknown> | null;\n onRetryTriage: () => Promise<void>;\n isRetrying: boolean;\n}\n\nexport function EscalationContextBlocks({\n isRoundsExhausted,\n payloadObj,\n isTerminal,\n resolverPayload,\n onRetryTriage,\n isRetrying,\n}: EscalationContextProps) {\n return (\n <>\n {/* Rounds-exhausted structured context */}\n {isRoundsExhausted && payloadObj && (\n <div className=\"mt-8\">\n <RoundsExhaustedContext\n payload={payloadObj}\n isTerminal={isTerminal}\n resolverPayload={resolverPayload}\n onRetryTriage={onRetryTriage}\n isRetrying={isRetrying}\n />\n </div>\n )}\n\n {/* Missing credential context */}\n {payloadObj?.category === 'missing_credential' && (\n <div className=\"mt-8 bg-status-warning/10 border border-status-warning/30 rounded-md px-5 py-4 flex items-start gap-3\">\n <KeyRound size={20} className=\"text-status-warning mt-0.5 shrink-0\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary mb-1\">Missing Credential</p>\n <p className=\"text-xs text-text-secondary mb-3\">\n This workflow requires a <span className=\"font-medium capitalize\">{String(payloadObj.provider)}</span> credential\n to continue. Register one and then resolve this escalation to retry.\n </p>\n <Link\n to=\"/credentials\"\n className=\"btn-primary text-xs inline-flex items-center gap-1.5\"\n >\n <KeyRound size={12} />\n Go to Credentials\n </Link>\n </div>\n </div>\n )}\n </>\n );\n}\n\ninterface FormSectionProps {\n esc: LTEscalationRecord;\n resolverPayload: unknown;\n isTerminal: boolean;\n /** The form is editable: the caller owns the claim, or (at a scan station)\n * the item is under a live claim and the badge moves to submit. */\n editable: boolean;\n activeView: ActiveView;\n metadataFormSchema: unknown;\n /** The resolved form schema (metadata-embedded, role-owned, or workflow fallback). */\n effectiveSchema: Record<string, unknown> | null;\n /** Resolved versioned knowledge lookups — the form's `lookup.*` context domain. */\n lookup?: Record<string, unknown> | null;\n json: string;\n onJsonChange: (v: string) => void;\n requestTriage: boolean;\n onRequestTriageChange: (v: boolean) => void;\n triageNotes: string;\n onTriageNotesChange: (v: string) => void;\n onResolve?: (payload: Record<string, unknown>) => void;\n onEscalate?: (targetRole: string) => void;\n onClaim?: () => void;\n submitAttempted?: boolean;\n isCertified?: boolean;\n hasAI?: boolean;\n /** Opens the Instructions side-panel view (form help icon). */\n onOpenHelp?: () => void;\n /** Fires when the user clicks the locked (unclaimed) form. */\n onDisabledClick?: () => void;\n /** Cursor-following hint over the locked form. */\n disabledHint?: React.ReactNode;\n}\n\n/**\n * The form area — the page's single, direct surface: the resolve form (or a\n * custom iframe viewport), shown even before claiming and enabled by the\n * claim. Terminal escalations render the submitted resolution read-only.\n * Everything else about the record lives in the side panel.\n */\nexport function EscalationFormSection({\n esc,\n resolverPayload,\n isTerminal,\n editable,\n activeView,\n metadataFormSchema,\n effectiveSchema,\n lookup,\n json,\n onJsonChange,\n requestTriage,\n onRequestTriageChange,\n triageNotes,\n onTriageNotesChange,\n onResolve,\n onEscalate,\n onClaim,\n submitAttempted,\n isCertified,\n hasAI,\n onOpenHelp,\n onDisabledClick,\n disabledHint,\n}: FormSectionProps) {\n const schema = effectiveSchema;\n const showIfCtx = buildShowIfContext(esc, lookup);\n\n // Terminal: show the submitted resolution read-only. The stored payload is\n // the NESTED shape (mapped through x-lt-bind on submit) — reverse-map it\n // back to flat form fields so the resolution renders as the same two-column\n // form it was filled in on, not as raw payload sections.\n const viewport = readViewport(schema);\n\n if (isTerminal) {\n const resolvedSrc = viewport && esc.status === 'resolved'\n ? viewportSrcForStage(viewport, VIEWPORT_STAGES.RESOLVED)\n : null;\n if (resolvedSrc) {\n return (\n <div className=\"mt-3\">\n <IframeViewport src={expandViewportSrc(resolvedSrc, esc)} escalation={esc} schema={schema!} stage={VIEWPORT_STAGES.RESOLVED} readOnly />\n </div>\n );\n }\n if (resolverPayload == null) return null;\n const payload = typeof resolverPayload === 'object' && resolverPayload !== null\n ? resolverPayload as Record<string, unknown>\n : {};\n // `$`-prefixed keys are control data ($accumulated, $trigger); the Items\n // view renders a collection, so a payload with nothing else shows no form.\n if (!Object.keys(payload).some((k) => !k.startsWith('$') && !k.startsWith('_'))) return null;\n const formSchema = (effectiveSchema ?? (metadataFormSchema as Record<string, any> | null));\n const value = formSchema?.properties\n ? { ...mapPayloadToForm(payload, formSchema), _form_schema: formSchema }\n : payload;\n return (\n <div className=\"mt-3\">\n <ResolverForm\n value={JSON.stringify(value, null, 2)}\n onChange={() => {}}\n disabled\n escalationContext={showIfCtx}\n disabledHint={disabledHint}\n />\n </div>\n );\n }\n\n if (activeView !== 'resolve' || !(esc.workflow_type || effectiveSchema)) return null;\n\n const isIframeViewport = !!viewport && !!onResolve && !!onEscalate;\n\n if (isIframeViewport && !editable) {\n const pendingSrc = viewportSrcForStage(viewport, VIEWPORT_STAGES.PENDING);\n if (pendingSrc) {\n return (\n <div className=\"mt-3\">\n <IframeViewport src={expandViewportSrc(pendingSrc, esc)} escalation={esc} schema={schema!} stage={VIEWPORT_STAGES.PENDING} readOnly />\n </div>\n );\n }\n return (\n <button\n onClick={() => onClaim?.()}\n disabled={!onClaim}\n className=\"flex flex-col items-center justify-center min-h-[55vh] w-full gap-3 text-center group disabled:opacity-50 disabled:cursor-default\"\n >\n <MonitorPlay className=\"w-10 h-10 text-text-tertiary group-hover:text-accent transition-colors\" strokeWidth={1} />\n <p className=\"text-base font-medium text-text-primary group-hover:text-accent transition-colors\">Claim to launch the editor</p>\n </button>\n );\n }\n\n const resolvedSrc = isIframeViewport ? expandViewportSrc(viewport.src, esc) : '';\n\n return (\n <div className=\"mt-3\">\n {isIframeViewport ? (\n <IframeViewport\n src={resolvedSrc}\n escalation={esc}\n schema={schema!}\n onResolve={onResolve!}\n onEscalate={onEscalate!}\n submitAttempted={submitAttempted}\n />\n ) : (\n <ResolverSection\n json={json}\n onJsonChange={onJsonChange}\n requestTriage={requestTriage}\n onRequestTriageChange={onRequestTriageChange}\n triageNotes={triageNotes}\n onTriageNotesChange={onTriageNotesChange}\n disabled={!editable}\n submitAttempted={submitAttempted}\n showTriage={!!isCertified && !!hasAI}\n escalationContext={showIfCtx}\n onOpenHelp={onOpenHelp}\n onDisabledClick={onDisabledClick}\n disabledHint={disabledHint}\n />\n )}\n </div>\n );\n}\n","import { useState, useCallback } from 'react';\nimport { Modal } from '../../../components/common/modal/Modal';\nimport { CustomDurationPicker } from '../../../components/common/form/CustomDurationPicker';\nimport { CountdownTimer } from '../../../components/common/display/CountdownTimer';\nimport { useClaimDurations } from '../../../hooks/useClaimDurations';\n\ninterface ClaimExpiryModalProps {\n open: boolean;\n /** ISO timestamp the claim expires at — drives the live countdown. */\n assignedUntil: string;\n onClose: () => void;\n onExtend: (durationMinutes: number) => void;\n isPending: boolean;\n}\n\n/**\n * Shown when the user's claim enters the warning window before expiry.\n * Extending re-claims with a fresh duration (an idempotent TTL extension for\n * the same assignee). Dismissing lets the claim lapse — the form locks at\n * expiry and edits are kept as a local draft.\n */\nexport function ClaimExpiryModal({ open, assignedUntil, onClose, onExtend, isPending }: ClaimExpiryModalProps) {\n const claimDurations = useClaimDurations();\n const [claimDuration, setClaimDuration] = useState('30');\n const [customMinutes, setCustomMinutes] = useState(0);\n\n const isCustom = claimDuration === 'custom';\n const effectiveMinutes = isCustom ? customMinutes : parseInt(claimDuration);\n const onCustomChange = useCallback((m: number) => setCustomMinutes(m), []);\n\n return (\n <Modal open={open} onClose={onClose} title=\"Claim Expiring\">\n <div className=\"space-y-4\">\n <p className=\"text-sm text-text-secondary\">\n Your claim on this escalation expires in{' '}\n <CountdownTimer until={assignedUntil} />. When it lapses, the form\n locks and the item returns to the queue — your edits are kept as a\n draft. Extend to keep working.\n </p>\n <select\n value={claimDuration}\n onChange={(e) => { setClaimDuration(e.target.value); setCustomMinutes(0); }}\n className=\"select w-full text-sm\"\n aria-label=\"Extension duration\"\n >\n {claimDurations.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n <option value=\"custom\">Other...</option>\n </select>\n {isCustom && (\n <CustomDurationPicker onChange={onCustomChange} autoFocus />\n )}\n <div className=\"flex justify-end gap-3 pt-2\">\n <button onClick={onClose} className=\"btn-secondary text-xs\">\n Dismiss\n </button>\n <button\n onClick={() => onExtend(effectiveMinutes)}\n className=\"btn-primary text-xs\"\n disabled={isPending || !effectiveMinutes || effectiveMinutes <= 0}\n >\n {isPending ? 'Extending...' : 'Extend Claim'}\n </button>\n </div>\n </div>\n </Modal>\n );\n}\n","import { useEffect, useState } from 'react';\n\n/** How long before claim expiry the extend prompt appears. */\nexport const CLAIM_WARNING_MS = 90_000;\n\nexport interface ClaimClock {\n /** The claim window has lapsed (`assigned_until` is in the past). */\n expired: boolean;\n /** Inside the warning window before expiry — time to offer an extension. */\n expiringSoon: boolean;\n /** Milliseconds until expiry; 0 when expired or when there is no claim. */\n msRemaining: number;\n}\n\n/**\n * Re-renders the consumer exactly when a claim crosses its warning threshold\n * and again when it expires, so claim-derived UI state (form lock, extend\n * dialog) reacts to the clock. Two one-shot timeouts per claim window — no\n * per-second interval. Timers reset whenever `assignedUntil` changes (a claim\n * extension moves the expiry forward and restarts the cycle).\n */\nexport function useClaimClock(\n assignedUntil: string | null | undefined,\n warningMs: number = CLAIM_WARNING_MS,\n): ClaimClock {\n const [, forceTick] = useState(0);\n\n useEffect(() => {\n if (!assignedUntil) return;\n const remaining = new Date(assignedUntil).getTime() - Date.now();\n if (remaining <= 0) return;\n const tick = () => forceTick((t) => t + 1);\n const timers: number[] = [];\n const untilWarning = remaining - warningMs;\n if (untilWarning > 0) timers.push(window.setTimeout(tick, untilWarning));\n timers.push(window.setTimeout(tick, remaining));\n return () => timers.forEach((t) => window.clearTimeout(t));\n }, [assignedUntil, warningMs]);\n\n if (!assignedUntil) return { expired: false, expiringSoon: false, msRemaining: 0 };\n const msRemaining = Math.max(0, new Date(assignedUntil).getTime() - Date.now());\n return {\n expired: msRemaining <= 0,\n expiringSoon: msRemaining > 0 && msRemaining <= warningMs,\n msRemaining,\n };\n}\n","/**\n * Local draft persistence for the resolver form. Edits are saved per\n * escalation while the user works and restored when they return — a lapsed\n * claim or an accidental navigation never loses typed input. The draft is\n * cleared when the escalation reaches a terminal state through this client\n * (resolved, acknowledged, or cancelled).\n */\n\nconst DRAFT_KEY_PREFIX = 'lt:escalation:draft:';\n\nfunction draftKey(escalationId: string): string {\n return `${DRAFT_KEY_PREFIX}${escalationId}`;\n}\n\nexport function readDraft(escalationId: string): string | null {\n try {\n return localStorage.getItem(draftKey(escalationId));\n } catch {\n return null;\n }\n}\n\nexport function saveDraft(escalationId: string, json: string): void {\n try {\n localStorage.setItem(draftKey(escalationId), json);\n } catch {\n /* storage unavailable or full — drafts are best-effort */\n }\n}\n\nexport function clearDraft(escalationId: string): void {\n try {\n localStorage.removeItem(draftKey(escalationId));\n } catch {\n /* storage unavailable — nothing to clear */\n }\n}\n","/**\n * x-lt-transition — post-submit hand-off UX. A form_schema may declare, at its\n * schema root, that submitting one of its escalations should pause on a wait\n * screen instead of returning to the previous page — because a follow-on\n * escalation is about to be born assigned to the submitter (a chained hand-off).\n *\n * x-lt-transition boolean — opt in to the wait screen\n * x-lt-transition-message markdown — shown on the wait screen\n * x-lt-transition-max-wait-seconds number — how long to wait for the\n * follow-on before running the fallback\n * query (clamped to a sane range)\n *\n * These tokens control ONLY the UX. Navigation to the follow-on is driven by the\n * engine's born-assigned `claimed` event (assigned_to + parent_id +\n * assigned_at_creation); the schema carries no navigation target and no metadata\n * is used.\n */\n\nexport const X_LT_TRANSITION = 'x-lt-transition';\nexport const X_LT_TRANSITION_MESSAGE = 'x-lt-transition-message';\nexport const X_LT_TRANSITION_MAX_WAIT_SECONDS = 'x-lt-transition-max-wait-seconds';\nexport const X_LT_TRANSITION_DONE = 'x-lt-transition-done';\n\nconst DEFAULT_MESSAGE = 'Submitted. Preparing your next step…';\nconst DEFAULT_MAX_WAIT_SECONDS = 30;\nconst MIN_MAX_WAIT_SECONDS = 5;\nconst MAX_MAX_WAIT_SECONDS = 300;\n\nexport interface TransitionConfig {\n /** Markdown shown on the wait screen. */\n message: string;\n /** Seconds to wait for the follow-on before the fallback query runs. */\n maxWaitSeconds: number;\n}\n\nfunction clampWaitSeconds(value: unknown): number {\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n) || n <= 0) return DEFAULT_MAX_WAIT_SECONDS;\n return Math.min(MAX_MAX_WAIT_SECONDS, Math.max(MIN_MAX_WAIT_SECONDS, Math.round(n)));\n}\n\n/**\n * The schema's transition config, or null when the schema does not opt in.\n * `x-lt-transition` must be truthy; message and max-wait fall back to defaults.\n */\nexport function readTransitionConfig(\n schema: Record<string, unknown> | null | undefined,\n): TransitionConfig | null {\n if (!schema || !schema[X_LT_TRANSITION]) return null;\n const rawMessage = schema[X_LT_TRANSITION_MESSAGE];\n const message =\n typeof rawMessage === 'string' && rawMessage.trim().length > 0 ? rawMessage : DEFAULT_MESSAGE;\n return { message, maxWaitSeconds: clampWaitSeconds(schema[X_LT_TRANSITION_MAX_WAIT_SECONDS]) };\n}\n\n/**\n * Where to go when this step ends without a further hand-off — the terminal\n * step of a chain (submitting it), or a transition step whose follow-on never\n * arrives (the wait times out). Independent of `x-lt-transition`, so a terminal\n * form can declare it without opting into the wait screen.\n *\n * The value is an href template using the same `{{domain.path}}` syntax and\n * routing conventions as `x-lt-href` (see LinkWidget): an internal path\n * (`/escalations/available?role=…`) navigates in-app; anything else opens\n * externally. A page reached via a forward transition can't rely on\n * `history.back()`, so this states the destination explicitly. Returns the raw\n * template (interpolate it against the escalation context before navigating),\n * or null when absent.\n */\nexport function readTransitionDone(\n schema: Record<string, unknown> | null | undefined,\n): string | null {\n const raw = schema?.[X_LT_TRANSITION_DONE];\n return typeof raw === 'string' && raw.trim().length > 0 ? raw : null;\n}\n","/**\n * x-lt-submit-on-claim — a one-gesture claim. A form_schema may declare, at its\n * schema root, that claiming one of its escalations should also resolve it in\n * the same gesture, submitting whatever the form currently holds (its seeded\n * defaults). The person clicks Claim once and the work item advances — no\n * second Submit step.\n *\n * x-lt-submit-on-claim boolean — opt in to claim-and-submit\n *\n * It is opt-in and defaults off. Pair it with `x-lt-labels` to rename the Claim\n * button (\"Claim and Submit\") so the single gesture reads honestly. When the\n * seeded defaults fail validation the claim still lands; the page drops into the\n * normal claimed state with the field errors surfaced, so the person finishes\n * the form by hand.\n */\n\nexport const X_LT_SUBMIT_ON_CLAIM = 'x-lt-submit-on-claim';\n\n/**\n * Whether the schema opts into claim-and-submit. Truthy `x-lt-submit-on-claim`\n * turns it on; anything else (absent, false) leaves the standard two-step claim\n * then submit.\n */\nexport function readSubmitOnClaim(\n schema: Record<string, unknown> | null | undefined,\n): boolean {\n return !!schema?.[X_LT_SUBMIT_ON_CLAIM];\n}\n","/**\n * x-lt-submit-guard — a query precondition on the resolve. A form_schema may\n * declare, at its root, that the escalation cannot be resolved while an embedded\n * escalation query still returns rows: the parent references a set of children\n * (commonly claimed by the same person) that must all be completed first, a\n * to-do list that empties as children resolve.\n *\n * \"x-lt-submit-guard\": {\n * \"query\": { \"role\": \"print-harvest\", \"status\": \"pending\",\n * \"assigned\": \"me\",\n * \"facets\": { \"walkId\": \"{{metadata.walkId}}\" } },\n * \"mustBeEmpty\": true, // the only mode today; defaults true\n * \"message\": \"{{count}} plates still pending — bag them before closing.\",\n * \"autoResolveWhenEmpty\": true // auto-submit the parent once empty\n * }\n *\n * The gate is isomorphic. The dashboard disables the submit while the query\n * returns rows (UI honesty), and the API layer enforces the same precondition\n * for `enforce_schema` roles so a raw-API resolve is rejected too. Only a\n * confirmed empty result (a successful read with zero rows) clears the gate — a\n * 403 or any error never does.\n *\n * `autoResolveWhenEmpty` closes the loop: the moment the query is confirmed\n * empty, the claimed parent auto-submits — checked on page-load and after each\n * inline child-resolve — so a set of escalations completes with no extra click.\n */\nimport type { EmbedQuery } from './x-lt-query';\n\nexport const X_LT_SUBMIT_GUARD = 'x-lt-submit-guard';\n\nexport interface SubmitGuardDef {\n query: EmbedQuery;\n /** The gate condition; defaults to true (the only supported mode today). */\n mustBeEmpty?: boolean;\n /** Shown beside the disabled submit. `{{count}}` and `{{domain.path}}` tokens interpolate. */\n message?: string;\n /** When true, auto-submit the claimed parent the moment the query is confirmed empty. */\n autoResolveWhenEmpty?: boolean;\n}\n\nexport const SUBMIT_GUARD_DEFAULT_MESSAGE = '{{count}} related items are still pending';\n\n/** The schema's submit-guard, or undefined when it declares none. */\nexport function readSubmitGuard(\n schema: Record<string, unknown> | null | undefined,\n): SubmitGuardDef | undefined {\n const raw = schema?.[X_LT_SUBMIT_GUARD];\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return undefined;\n const guard = raw as SubmitGuardDef;\n if (!guard.query || typeof guard.query !== 'object') return undefined;\n return guard;\n}\n\n/**\n * Whether a guard blocks the resolve given the query's row count. `mustBeEmpty`\n * defaults to true, so any matching row blocks. A pure decision — the caller\n * supplies the count from a confirmed read (never an error/loading state).\n */\nexport function guardBlocks(count: number, guard: SubmitGuardDef | undefined): boolean {\n return !!guard && guard.mustBeEmpty !== false && count > 0;\n}\n","import { useEscalations } from '../api/escalations';\nimport { useAuth } from './useAuth';\nimport { interpolateHelp } from '../lib/x-lt-help';\nimport { resolveScopedQuery } from '../lib/x-lt-query';\nimport { SUBMIT_GUARD_DEFAULT_MESSAGE, type SubmitGuardDef } from '../lib/x-lt-submit-guard';\nimport type { ShowIfContext } from '../lib/x-lt-show-if';\n\nexport type { SubmitGuardDef } from '../lib/x-lt-submit-guard';\n\n/**\n * The `x-lt-submit-guard` gate on the resolve button. While the embedded query\n * still returns rows the submit stays disabled and `message` renders the live\n * count; the moment the last row resolves, the existing socket-invalidation\n * path refires the query and the button lights up. No polling.\n *\n * Only a CONFIRMED empty read clears the gate — a successful query returning\n * zero rows. Loading, an error, or a 403 never clear it: the button stays\n * disabled until the guard can prove there is nothing left. `confirmedEmpty`\n * is the same signal the page's auto-resolve waits on.\n *\n * The server enforces the identical precondition for `enforce_schema` roles\n * (services/escalation/submit-guard.ts), so a raw-API resolve is rejected too.\n */\nexport interface SubmitGuardState {\n blocked: boolean;\n count: number;\n message: string;\n /** True only on a successful read with zero rows — never while loading or errored. */\n confirmedEmpty: boolean;\n}\n\nexport function useSubmitGuard(\n guard: SubmitGuardDef | undefined,\n ctx: ShowIfContext | undefined,\n): SubmitGuardState {\n const { user } = useAuth();\n\n // The SAME mapping the escalation-list embed uses (resolveScopedQuery) —\n // including the `assigned` ownership scope — so the guard's count always\n // matches the rows a same-query embed renders.\n const scoped = resolveScopedQuery(guard?.query ?? {}, ctx ?? {}, user?.userId);\n\n const { data, isSuccess } = useEscalations({\n role: scoped.role,\n status: scoped.status,\n facets: scoped.facets,\n assigned_to: scoped.assigned_to,\n available: scoped.available,\n limit: 1, // the gate needs only the total; rows are irrelevant\n enabled: !!guard && scoped.enabled,\n });\n\n // A guard whose scope cannot be evaluated (no role/facets, or `assigned:'me'`\n // with no viewer) is inert — it neither blocks nor auto-resolves. When the\n // scope IS evaluable, only a successful empty read clears the gate.\n const active = !!guard && guard.mustBeEmpty !== false && scoped.enabled;\n const count = data?.total ?? 0;\n const confirmedEmpty = active && isSuccess && count === 0;\n const blocked = active && !confirmedEmpty;\n\n // {{count}} is a local token — substitute it BEFORE the domain pass, which\n // replaces unknown domains with the missing-value dash. While blocked without\n // a confirmed count (loading/error), a count message would lie, so show a\n // neutral checking line instead.\n let message = '';\n if (blocked) {\n if (count > 0) {\n const template = guard?.message ?? SUBMIT_GUARD_DEFAULT_MESSAGE;\n message = interpolateHelp(template.replace(/\\{\\{count\\}\\}/g, String(count)), ctx ?? {});\n } else {\n message = 'Checking related items…';\n }\n }\n\n return { blocked, count, message, confirmedEmpty };\n}\n","import { createPortal } from 'react-dom';\nimport { MarkdownRenderer } from '../common/display/MarkdownRenderer';\n\ninterface TransitionWaitModalProps {\n open: boolean;\n /** Markdown shown while the follow-on is being prepared. */\n message: string;\n}\n\n/**\n * The hand-off bridge. After the viewer submits an escalation whose form opted\n * in via `x-lt-transition`, this holds the screen while the workflow creates and\n * assigns the next step. It is intentionally NOT dismissible — there is no wrong\n * button to press; either the follow-on arrives (useFollowMyClaims navigates and\n * this unmounts) or the detail page's timeout resolves it. Centered at every\n * breakpoint: a full-width sheet on narrow screens, a centered card on wide.\n */\nexport function TransitionWaitModal({ open, message }: TransitionWaitModalProps) {\n if (!open) return null;\n\n return createPortal(\n <div className=\"fixed inset-0 z-50 flex items-center justify-center px-4\">\n <div className=\"absolute inset-0 bg-text-primary/40 backdrop-blur-sm\" />\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"relative w-full max-w-sm bg-surface-raised border border-surface-border rounded-lg shadow-xl px-6 py-7 text-center\"\n >\n <div className=\"flex justify-center mb-4\" aria-hidden=\"true\">\n <span className=\"inline-block w-7 h-7 rounded-full border-2 border-surface-border border-t-accent animate-spin\" />\n </div>\n <div className=\"text-sm text-text-secondary [&_p]:m-0 [&_p+p]:mt-2\">\n <MarkdownRenderer content={message} />\n </div>\n </div>\n </div>,\n document.body,\n );\n}\n","import { IdCard, ScanBarcode } from 'lucide-react';\nimport { Modal } from '../../common/modal/Modal';\nimport { UserName } from '../../common/display/UserName';\n\n/**\n * The station write stop-over: editing is open, but a state-changing write owes\n * a fresh badge. Holds the moment and names who to badge as and what the badge\n * will do; the detail page runs the stashed action once a matching badge primes.\n */\nexport function StationWriteChallenge({\n open,\n verb = 'submit',\n claimantId,\n wrongBadgeName,\n onCancel,\n}: {\n open: boolean;\n verb?: string;\n claimantId?: string | null;\n wrongBadgeName?: string | null;\n onCancel: () => void;\n}) {\n if (!open) return null;\n return (\n <Modal open onClose={onCancel} title={`Scan your badge to ${verb}`}>\n <div className=\"flex flex-col items-center text-center py-6\" data-testid=\"station-write-challenge\">\n <div className=\"flex items-center gap-4 mb-6\">\n <ScanBarcode className=\"w-7 h-7 text-text-quaternary\" strokeWidth={1} />\n <IdCard className=\"w-9 h-9 text-accent-muted\" strokeWidth={1} />\n </div>\n <p className=\"text-lg text-text-secondary\">\n Scan your badge to {verb} as{' '}\n <span className=\"font-medium text-text-primary\">\n {claimantId ? <UserName userId={claimantId} /> : 'the claimant'}\n </span>.\n </p>\n {wrongBadgeName && (\n <p className=\"text-sm text-status-error mt-3\" data-testid=\"wrong-badge\">\n That badge is {wrongBadgeName}'s — scan the claimant's badge to {verb}.\n </p>\n )}\n <div className=\"w-16 border-t border-surface-border my-6\" />\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"text-sm text-text-tertiary hover:text-text-secondary hover:underline\"\n >\n Cancel\n </button>\n </div>\n </Modal>\n );\n}\n","import { useState, useEffect, useMemo, useRef } from 'react';\nimport { useParams, useNavigate, useLocation } from 'react-router-dom';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { useAuth } from '../../../hooks/useAuth';\nimport { useAccess } from '../../../hooks/useAccess';\nimport { useActingIdentity } from '../../../hooks/useActingIdentity';\nimport { useScanEnabled } from '../../../hooks/useScanInput';\nimport {\n useEscalation,\n useEscalationLookups,\n useClaimEscalation,\n useResolveEscalation,\n useEscalateToRole,\n useCancelEscalation,\n useBulkAssignEscalations,\n useBulkUnassignEscalations,\n} from '../../../api/escalations';\nimport { BulkAssignModal } from '../../../components/common/modal/BulkAssignModal';\nimport { ApiError } from '../../../api/client';\nimport { isValidationErrorBody } from '../../../lib/validation';\nimport { ConfirmCancelModal } from '../../../components/common/modal/ConfirmCancelModal';\nimport { ScanConfirmModal } from '../../../components/scan/ScanConfirmModal';\nimport { PageHeader } from '../../../components/common/layout/PageHeader';\nimport { ListToolbar } from '../../../components/common/data/ListToolbar';\nimport { isEffectivelyClaimed } from '../../../lib/escalation';\nimport { seedFormJson } from '../../../lib/seed-form-json';\nimport { useWorkflowConfigs } from '../../../api/workflows';\nimport { useSettings } from '../../../api/settings';\nimport { getAiOverride } from '../../../lib/view-as';\nimport { useEscalationDetailEvents } from '../../../hooks/useEventHooks';\nimport { PanelRightClose, PanelRightOpen, RotateCcw, X } from 'lucide-react';\nimport { EscalationSidePanel, ESCALATION_PANEL_VIEWS } from '../../../components/escalation/EscalationSidePanel';\nimport { EscalationActionBar } from './EscalationActionBar';\nimport type { ActionBarMode, ActiveView } from './EscalationActionBar';\nimport { UserName } from '../../../components/common/display/UserName';\nimport type { FieldError } from '../../../lib/field-validator';\nimport { validateResolverForm } from '../../../lib/field-validator';\nimport { EscalationContextBlocks, EscalationFormSection, buildShowIfContext } from './EscalationDetailSections';\nimport { VIEWPORT_STAGES, expandViewportSrc, readViewport } from '../../../lib/x-lt-viewport';\nimport { isReadOnlyLogin } from '../../../lib/station-login';\nimport { useMyRoles } from '../../../api/users';\nimport { IframeViewport } from '../../../components/escalation/IframeViewport';\nimport { ClaimExpiryModal } from './ClaimExpiryModal';\nimport { useClaimClock } from '../../../hooks/useClaimClock';\nimport { readDraft, saveDraft, clearDraft } from '../../../lib/draft-store';\nimport { readTransitionConfig, readTransitionDone } from '../../../lib/x-lt-transition';\nimport { readSubmitOnClaim } from '../../../lib/x-lt-submit-on-claim';\nimport { readFooterLabels } from '../../../lib/x-lt-labels';\nimport { readSubmitGuard } from '../../../lib/x-lt-submit-guard';\nimport { useSubmitGuard } from '../../../hooks/useSubmitGuard';\nimport { buildResolverPayload } from '../../../lib/resolver-payload';\nimport { interpolateHelp } from '../../../lib/x-lt-help';\nimport { TransitionWaitModal } from '../../../components/escalation/TransitionWaitModal';\nimport { StationWriteChallenge } from '../../../components/scan/station/StationWriteChallenge';\nimport { apiFetch } from '../../../api/client';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst PANEL_OPEN_KEY = 'lt:escalation:panel:open';\n\nfunction readPanelOpen(): boolean {\n try {\n const v = localStorage.getItem(PANEL_OPEN_KEY);\n return v === null ? true : v === 'true';\n } catch {\n return true;\n }\n}\n\nfunction savePanelOpen(open: boolean): void {\n try { localStorage.setItem(PANEL_OPEN_KEY, String(open)); } catch {}\n}\n\nfunction safeParse(value: string | null | undefined): unknown {\n if (!value) return null;\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Main page\n// ---------------------------------------------------------------------------\n\nexport function EscalationDetailPage() {\n // The detail route reuses this element across navigations to other\n // escalations (same route pattern, different :id). Keying the view by id\n // tears down and rebuilds ALL local state — the seeded form json, the\n // one-shot init ref, submit flags — so nothing rendered for one escalation\n // can survive into, or act on, another.\n const { id } = useParams<{ id: string }>();\n return <EscalationDetailView key={id} id={id!} />;\n}\n\nfunction EscalationDetailView({ id }: { id: string }) {\n const { user, isSuperAdmin, hasRoleType } = useAuth();\n // Claim overrides (reassign / return to queue) are management verbs.\n const canManage = isSuperAdmin || hasRoleType('admin');\n const { identity: acting, clear: clearActing } = useActingIdentity();\n const scanEnabled = useScanEnabled();\n // Memberships come from the DB so the read-only-station test holds whatever the login method.\n const { data: myRoles } = useMyRoles(user?.userId ?? null);\n // The badge grant outranks the session: whoever badged in owns the claim\n // comparisons here, and the mutations they fire ride the acting header so\n // the server attributes them to the same person.\n const effectiveActorId = acting?.actorId ?? user?.userId;\n const navigate = useNavigate();\n const location = useLocation();\n const queryClient = useQueryClient();\n const { data: esc, isLoading, refetch, isFetching } = useEscalation(id);\n useEscalationDetailEvents(id);\n // Versioned knowledge lookups pinned on the row's envelope — one immutable\n // batch fetch, folded into the form context as the `lookup.*` domain.\n // Cascade levels resolve locally from this data; no network during edits.\n const hasLookupRefs = useMemo(() => {\n if (!esc?.envelope) return false;\n try {\n const parsed = JSON.parse(esc.envelope);\n return Array.isArray(parsed?.lookups) && parsed.lookups.length > 0;\n } catch {\n return false;\n }\n }, [esc?.envelope]);\n const { data: lookupData } = useEscalationLookups(id, hasLookupRefs);\n const lookupCtx = useMemo(() => {\n if (!lookupData?.lookups?.length) return null;\n return Object.fromEntries(\n lookupData.lookups.filter((l) => !l.missing).map((l) => [l.as ?? l.key, l.data]),\n );\n }, [lookupData]);\n const claim = useClaimEscalation();\n const resolve = useResolveEscalation();\n const escalate = useEscalateToRole();\n const cancel = useCancelEscalation();\n const bulkAssign = useBulkAssignEscalations();\n const bulkUnassign = useBulkUnassignEscalations();\n const [cancelModalOpen, setCancelModalOpen] = useState(false);\n const [reassignModalOpen, setReassignModalOpen] = useState(false);\n const { data: workflowConfigs } = useWorkflowConfigs();\n const { data: settings } = useSettings();\n\n const { isBuilder } = useAccess();\n\n const wfConfig = workflowConfigs?.find((c) => c.workflow_type === esc?.workflow_type);\n const traceUrl = settings?.telemetry?.traceUrl ?? null;\n const [activeView, setActiveView] = useState<ActiveView>('resolve');\n const [json, setJson] = useState('{}');\n\n const [sidePanelOpen, setSidePanelOpen] = useState<boolean>(readPanelOpen);\n\n const [requestTriage, setRequestTriage] = useState(false);\n const [triageNotes, setTriageNotes] = useState('');\n const [submitAttempted, setSubmitAttempted] = useState(false);\n const [formErrors, setFormErrors] = useState<FieldError[]>([]);\n // Bumped on every click on the locked (unclaimed) form; each bump replays\n // the claim button's wiggle — the answer to \"why can't I type here.\"\n const [claimNudge, setClaimNudge] = useState(0);\n const [panelActiveView, setPanelActiveView] = useState<string | undefined>(undefined);\n // Set after a successful resolve when the form opted into a hand-off\n // (x-lt-transition). Holds the screen on a wait modal instead of returning to\n // the list; the global useFollowMyClaims navigates to the born-assigned\n // follow-on when its `claimed` event arrives. The timeout below is the\n // fallback for the SDK's at-most-once delivery (a dropped event); `doneDest`\n // is where to land if the follow-on never comes (an x-lt-transition-done URL).\n const [waiting, setWaiting] = useState<{ message: string; maxWaitSeconds: number; doneDest: string | null } | null>(null);\n\n // Auto-start (list-driven claim-and-submit): a list row launched with the\n // `autoStart` intent (x-lt-row-action submitOnClaim) claims and submits this\n // escalation without a second gesture, then transitions on to the follow-on.\n // `autoStarting` masks the brief form render with the wait screen; the ref\n // holds the latest handler (assigned in the render body, where the handlers\n // are defined) so the effect below can fire it exactly once.\n const [autoStarting, setAutoStarting] = useState(false);\n const autoStartFiredRef = useRef(false);\n const autoStartActionRef = useRef<(() => void) | null>(null);\n\n // Auto-resolve-when-empty (x-lt-submit-guard.autoResolveWhenEmpty): the moment\n // the guard query is confirmed empty, the claimed parent submits itself. Fires\n // once; the effect re-checks on page-load and after each inline child-resolve\n // (the guard query is socket-invalidated, so it refetches with no polling).\n const autoResolveFiredRef = useRef(false);\n const autoResolveActionRef = useRef<(() => void) | null>(null);\n\n const [pendingWrite, setPendingWrite] = useState<{ verb: string; run: () => void | Promise<void> } | null>(null);\n\n // Claim clock: re-renders at the warning threshold (extend prompt) and at\n // expiry (isEffectivelyClaimed flips false on that render — the form locks\n // and the action bar returns to its available state). Dismissal is keyed by\n // the assigned_until value so an ignored prompt stays away for that claim\n // window but returns after an extension starts a new one.\n const claimClock = useClaimClock(esc?.assigned_until);\n const [extendDismissedUntil, setExtendDismissedUntil] = useState<string | null>(null);\n\n // Recompute form errors in real-time once the user has attempted a submit.\n // This keeps the errors sidebar in sync as the user fixes (or breaks) fields.\n useEffect(() => {\n if (!submitAttempted || !esc) return;\n try {\n const payload = JSON.parse(json) as Record<string, unknown>;\n const schema = payload._form_schema as Record<string, unknown> | undefined;\n if (!schema) { setFormErrors([]); return; }\n setFormErrors(validateResolverForm(schema, payload, buildShowIfContext(esc, lookupCtx)));\n } catch { /* leave errors unchanged on parse failure */ }\n }, [json, submitAttempted, esc]);\n // Schema resolution, most specific first:\n // 1. metadata.form_schema — a full form embedded on the row (legacy records)\n // 2. esc.form_schema — the role's form the single-escalation GET already\n // JOINed in, resolved to the row's pinned version (metadata.schema_version)\n // or the role's latest when unpinned. No second call.\n // 3. workflow-level resolver_schema (legacy fallback)\n const metadataFormSchema = (esc?.metadata as any)?.form_schema ?? null;\n const resolverSchema =\n (esc?.form_schema ?? wfConfig?.resolver_schema ?? null) as Record<string, any> | null;\n const effectiveSchema = metadataFormSchema ?? resolverSchema;\n\n // Footer configuration — root tokens on the form schema. `x-lt-submit-on-claim`\n // folds claim and submit into one gesture; `x-lt-labels` renames the action\n // controls (e.g. a \"Claim and Submit\" button when both are set).\n const submitOnClaim = readSubmitOnClaim(effectiveSchema as Record<string, unknown> | null);\n const footerLabels = readFooterLabels(effectiveSchema as Record<string, unknown> | null);\n\n // x-lt-submit-guard — the query precondition. The page owns it (not the action\n // bar) because it also drives auto-resolve-when-empty. Inert until the row\n // loads; the hook self-disables when the schema declares no guard.\n const submitGuardDef = readSubmitGuard(effectiveSchema as Record<string, unknown> | null);\n const submitGuard = useSubmitGuard(submitGuardDef, esc ? buildShowIfContext(esc, lookupCtx) : undefined);\n\n // Initialize json from the form exactly once. Fields are seeded in priority order:\n // metadata (facts stamped at enqueue) → envelope.formDefaults (workflow overrides)\n // → schema default. formDefaults always wins over metadata so the workflow can\n // override a metadata fact when needed. Subsequent esc refetches must NOT reset\n // user edits. The form arrives embedded on esc, so nothing else to await.\n const jsonInitialized = useRef(false);\n const initialJsonRef = useRef<string | null>(null);\n useEffect(() => {\n if (jsonInitialized.current) return;\n const formSchema = metadataFormSchema ?? (resolverSchema?.properties ? resolverSchema : null);\n if (formSchema?.properties) {\n jsonInitialized.current = true;\n const seeded = safeParse(esc?.envelope) as Record<string, any> | null;\n const formDefaults = seeded?.formDefaults;\n const mergedPrefill: Record<string, any> = {\n ...(esc?.metadata ?? {}),\n ...(typeof formDefaults === 'object' && formDefaults !== null ? formDefaults : {}),\n };\n initialJsonRef.current = seedFormJson(formSchema, mergedPrefill);\n // A saved draft (typed input from an earlier visit or a lapsed claim)\n // wins over the seeded defaults. The schema is always taken fresh —\n // a draft never resurrects a stale form definition.\n const terminal = esc?.status === 'resolved' || esc?.status === 'cancelled';\n const draft = !terminal && esc?.id ? readDraft(esc.id) : null;\n const draftObj = draft ? (safeParse(draft) as Record<string, any> | null) : null;\n if (draftObj && typeof draftObj === 'object' && !Array.isArray(draftObj)) {\n setJson(JSON.stringify({ ...draftObj, _form_schema: formSchema }, null, 2));\n } else {\n setJson(initialJsonRef.current);\n }\n } else if (effectiveSchema) {\n jsonInitialized.current = true;\n setJson(JSON.stringify(effectiveSchema, null, 2));\n }\n }, [effectiveSchema, metadataFormSchema, resolverSchema, esc?.envelope, esc?.metadata, esc?.id, esc?.status]);\n\n // Persist edits as a local draft (debounced). Best-effort insurance against\n // a lapsed claim or accidental navigation. Pristine defaults are not saved,\n // and reverting to them removes the stored draft; a terminal outcome\n // through this client clears it too (see handleResolve / handleConfirmCancel).\n useEffect(() => {\n if (!jsonInitialized.current || initialJsonRef.current === null || !esc?.id) return;\n if (esc.status !== 'pending') return;\n const escalationId = esc.id;\n const timer = window.setTimeout(() => {\n if (json === initialJsonRef.current) clearDraft(escalationId);\n else saveDraft(escalationId, json);\n }, 400);\n return () => window.clearTimeout(timer);\n }, [json, esc?.id, esc?.status]);\n\n // Hand-off fallback. The follow-on's `claimed` event normally arrives and\n // useFollowMyClaims navigates (this view unmounts, clearing the timer). SDK\n // delivery is at-most-once, so if that event was dropped, on timeout we query\n // the child of this escalation assigned to the viewer — precise, no heuristic\n // — and land on it; only if none exists do we return to the list.\n useEffect(() => {\n if (!waiting) return;\n const timer = window.setTimeout(async () => {\n try {\n const params = new URLSearchParams({\n parent_id: id,\n assigned_to: effectiveActorId ?? '',\n status: 'pending',\n limit: '1',\n });\n const res = await apiFetch<{ escalations: Array<{ id: string; parent_id?: string }> }>(`/escalations?${params}`);\n const child = res?.escalations?.[0];\n // Guard the correlation on the row itself, so a query path that does not\n // filter by parent_id (self-scope search) can never land a wrong child.\n if (child?.id && child.parent_id === id) {\n setWaiting(null);\n navigate(`/escalations/detail/${child.id}`);\n return;\n }\n } catch { /* fall through to the declared destination / previous page */ }\n setWaiting(null);\n queryClient.resetQueries({ queryKey: ['escalations'] });\n queryClient.resetQueries({ queryKey: ['escalationStats'] });\n // No follow-on arrived: prefer the form's declared destination over\n // history.back() (which is wrong for a page reached via a transition).\n const dest = waiting.doneDest;\n if (dest) {\n if (dest.startsWith('/')) navigate(dest);\n else window.location.assign(dest);\n } else {\n navigate(-1);\n }\n }, waiting.maxWaitSeconds * 1000);\n return () => window.clearTimeout(timer);\n }, [waiting, id, effectiveActorId, navigate, queryClient]);\n\n // Fire the list-driven auto-start once. The escalation must be loaded, still\n // claimable (pending, no live claim), and — when it carries a form — seeded so\n // its defaults are ready to submit. The one-shot ref survives refetches; the\n // action ref holds the current handler closure (assigned in the render body,\n // after the handlers are defined).\n useEffect(() => {\n if (autoStartFiredRef.current) return;\n if (!(location.state as { autoStart?: boolean } | null)?.autoStart) return;\n if (!esc || esc.status !== 'pending' || isEffectivelyClaimed(esc)) return;\n // The seeded defaults land in `json` one render after the schema resolves;\n // an unseeded '{}' would submit an empty payload, so wait for the real form.\n if (json === '{}') return;\n autoStartFiredRef.current = true;\n autoStartActionRef.current?.();\n }, [esc, json, location.state]);\n\n // Auto-resolve the claimed parent once the submit guard confirms its children\n // are gone. Only the claimant closes it, and only when the parent's own form\n // has seeded — the action ref (below) revalidates before submitting.\n useEffect(() => {\n if (autoResolveFiredRef.current) return;\n if (!submitGuardDef?.autoResolveWhenEmpty || !submitGuard.confirmedEmpty) return;\n if (!esc || esc.status !== 'pending' || !isEffectivelyClaimed(esc)) return;\n if (esc.assigned_to !== effectiveActorId) return;\n if (json === '{}') return;\n autoResolveFiredRef.current = true;\n autoResolveActionRef.current?.();\n }, [submitGuard.confirmedEmpty, submitGuardDef, esc, json, effectiveActorId]);\n\n useEffect(() => {\n if (!pendingWrite || !esc) return;\n if (acting && acting.actorId === esc.assigned_to) {\n const { run } = pendingWrite;\n setPendingWrite(null);\n void run();\n }\n }, [acting, pendingWrite, esc?.assigned_to]);\n\n const isRoundsExhausted = esc?.subtype === 'rounds_exhausted';\n\n if (isLoading) {\n return (\n <div className=\"animate-pulse space-y-8\">\n <div className=\"h-8 bg-surface-sunken rounded w-64\" />\n <div className=\"h-32 bg-surface-sunken rounded w-full\" />\n </div>\n );\n }\n\n if (!esc) {\n return <p className=\"text-sm text-text-secondary\">Escalation not found.</p>;\n }\n\n const claimed = isEffectivelyClaimed(esc);\n const claimedByMe = claimed && esc.assigned_to === effectiveActorId;\n const claimedByOther = claimed && !claimedByMe;\n const isTerminal = esc.status === 'resolved' || esc.status === 'cancelled';\n\n // Only a read-only station login works another person's claim and badges at\n // submit. A login with its own write authority keeps the standard bar.\n const stationWorkable = scanEnabled && isReadOnlyLogin(myRoles) && claimed && !isTerminal;\n const editable = claimedByMe || stationWorkable;\n const writeNeedsBadge = stationWorkable && esc.assigned_to !== user?.userId;\n\n const iframeViewport = readViewport(effectiveSchema as Record<string, unknown> | null);\n const isIframeMode = !!iframeViewport && editable && !isTerminal;\n\n const escalationPayload = safeParse(esc.escalation_payload);\n const resolverPayload = safeParse(esc.resolver_payload);\n const envelopeObj = safeParse(esc.envelope) as Record<string, any> | null;\n const isCertified = !!(envelopeObj?.metadata?.certified);\n const aiOverride = getAiOverride();\n const hasAI = aiOverride !== null ? aiOverride : !!(settings as any)?.ai?.enabled;\n\n const payloadObj = (typeof escalationPayload === 'object' && escalationPayload !== null && !Array.isArray(escalationPayload))\n ? escalationPayload as Record<string, unknown>\n : null;\n const triageData = payloadObj?._triage as Record<string, unknown> | undefined;\n const resolverObj = (typeof resolverPayload === 'object' && resolverPayload !== null && !Array.isArray(resolverPayload))\n ? resolverPayload as Record<string, unknown>\n : null;\n\n const actionBarMode: ActionBarMode = isTerminal\n ? 'terminal'\n : claimedByMe || stationWorkable\n ? 'claimed_by_me'\n : claimedByOther\n ? 'claimed_by_other'\n : 'available';\n\n // The locked form's cursor hint — names the state or the unlocking gesture\n // (in the role's own vocabulary) at the exact point of interaction.\n const lockedFormHint: React.ReactNode =\n actionBarMode === 'available'\n ? `${typeof footerLabels.claim === 'string' ? footerLabels.claim : 'Claim'} this escalation to edit the form`\n : actionBarMode === 'claimed_by_other'\n ? <>Claimed by {esc.assigned_to ? <UserName userId={esc.assigned_to} /> : 'another user'}</>\n : esc.status === 'cancelled'\n ? 'This escalation is cancelled'\n : esc.status === 'resolved'\n ? 'This escalation is resolved — the submitted values are shown'\n : null;\n\n // Every mutation below targets the mount-fixed route id, never esc.id.\n // The two are equal for the life of this keyed view, but binding to the\n // prop makes it impossible for an in-flight render against refreshed\n // query data to redirect an action at a different escalation.\n const handleClaim = (durationMinutes: number) => {\n claim.mutate({ id, durationMinutes });\n };\n\n const goBack = () => {\n queryClient.resetQueries({ queryKey: ['escalations'] });\n queryClient.resetQueries({ queryKey: ['escalationStats'] });\n navigate(-1);\n };\n\n // The declared x-lt-transition-done destination for this form, interpolated\n // against the escalation context ({{metadata.account}} etc.), or null.\n const resolveDoneDest = (): string | null => {\n const tmpl = readTransitionDone(effectiveSchema as Record<string, unknown> | null);\n if (!tmpl) return null;\n return interpolateHelp(tmpl, buildShowIfContext(esc, lookupCtx)) || null;\n };\n\n // Navigate to a resolved destination — internal path in-app, else external —\n // refreshing the queue caches first (as goBack does).\n const navigateDone = (dest: string) => {\n queryClient.resetQueries({ queryKey: ['escalations'] });\n queryClient.resetQueries({ queryKey: ['escalationStats'] });\n if (dest.startsWith('/')) navigate(dest);\n else window.location.assign(dest);\n };\n\n const submitResolve = async (payload: Record<string, unknown>) => {\n try {\n await resolve.mutateAsync({ id, resolverPayload: payload });\n } catch (err) {\n // Server-side schema enforcement (enforce_schema roles) — the 422 body\n // carries the same field-error list the pre-submission pass produces;\n // route it into the same errors panel.\n if (err instanceof ApiError && isValidationErrorBody(err.body)) {\n setSubmitAttempted(true);\n setFormErrors(err.body.violations);\n setPanelActiveView(ESCALATION_PANEL_VIEWS.ERRORS);\n return;\n }\n throw err;\n }\n clearDraft(id);\n // Hand-off resolution, in order:\n // 1. x-lt-transition → hold on the wait screen while the born-assigned\n // follow-on is prepared (doneDest is where to land if it never comes).\n // 2. x-lt-transition-done → an explicit destination. A terminal chain step\n // was reached by a forward navigation, so history.back() is wrong; go\n // where the form says (a canonical page or a rich worklist URL).\n // 3. neither → return to the previous page.\n const transition = readTransitionConfig(effectiveSchema as Record<string, unknown> | null);\n const doneDest = resolveDoneDest();\n if (transition) {\n setWaiting({ ...transition, doneDest });\n return;\n }\n if (doneDest) {\n navigateDone(doneDest);\n return;\n }\n goBack();\n };\n\n const guardStationWrite = (verb: string, run: () => void | Promise<void>) => {\n if (writeNeedsBadge) {\n clearActing();\n setPendingWrite({ verb, run });\n return;\n }\n void run();\n };\n\n const handleResolve = async (payload: Record<string, unknown>) => {\n guardStationWrite('submit', () => submitResolve(payload));\n };\n\n // x-lt-submit-on-claim: claim, then immediately resolve with whatever the form\n // holds (its seeded defaults). The two are distinct operations — the row must\n // be claimed by me before the server accepts the resolve — so they run in\n // sequence. If the defaults fail validation the claim still stands; the page\n // drops into the normal claimed state with the errors surfaced so the person\n // finishes by hand. Wired to the Claim button only; extend and the locked-form\n // nudge keep the plain claim.\n const handleClaimAndSubmit = async (durationMinutes: number) => {\n try {\n await claim.mutateAsync({ id, durationMinutes });\n } catch {\n return; // claim.error surfaces through the action bar\n }\n // A submit guard defers the auto-submit: land the person on the claimed\n // parent with its children to work; the auto-resolve effect closes it once\n // they clear (pair submitOnClaim with autoResolveWhenEmpty for full flow).\n if (submitGuard.blocked) return;\n const result = buildResolverPayload(json, buildShowIfContext(esc, lookupCtx));\n if (result.parseError) return; // seeded form is malformed — leave it claimed\n if (result.errors.length > 0) {\n setSubmitAttempted(true);\n setFormErrors(result.errors);\n setSidePanelOpen(true);\n savePanelOpen(true);\n setPanelActiveView(ESCALATION_PANEL_VIEWS.ERRORS);\n return;\n }\n await handleResolve(result.payload!);\n };\n\n // The list-driven auto-start: mask the form with the wait screen, then run the\n // same claim-and-submit the manual button runs. On success the transition (or\n // goBack) takes over; on a validation miss the mask drops to reveal the\n // claimed form and its errors. Reassigned each render so the pre-return effect\n // fires the latest closure.\n autoStartActionRef.current = async () => {\n const dm = (location.state as { durationMinutes?: number } | null)?.durationMinutes;\n setAutoStarting(true);\n await handleClaimAndSubmit(typeof dm === 'number' && dm > 0 ? dm : 30);\n setAutoStarting(false);\n };\n\n // Auto-resolve-when-empty: submit the parent's seeded payload once the guard\n // clears. Revalidates first — if the parent's own form is incomplete, leave it\n // for the person rather than auto-closing an invalid record.\n autoResolveActionRef.current = () => {\n const result = buildResolverPayload(json, buildShowIfContext(esc, lookupCtx));\n if (result.parseError || result.errors.length > 0) return;\n handleResolve(result.payload!);\n };\n\n const handleEscalate = (targetRole: string) => {\n if (!targetRole) return;\n guardStationWrite('escalate', async () => {\n await escalate.mutateAsync({ id, targetRole });\n goBack();\n });\n };\n\n const handleRetryTriage = async () => {\n if (!claimedByMe) {\n await claim.mutateAsync({ id, durationMinutes: 30 });\n }\n const diagnosis = (payloadObj?.diagnosis as string) || esc.description || '';\n await resolve.mutateAsync({\n id,\n resolverPayload: { _lt: { needsTriage: true }, notes: diagnosis },\n });\n goBack();\n };\n\n const handleRelease = () => {\n guardStationWrite('release', async () => {\n await claim.mutateAsync({ id, durationMinutes: 0 });\n goBack();\n });\n };\n\n const handleConfirmCancel = () => {\n setCancelModalOpen(false);\n guardStationWrite('cancel', async () => {\n await cancel.mutateAsync(id);\n clearDraft(id);\n goBack();\n });\n };\n\n // One panel entry in the header: the toggle. Authored help stays reachable\n // through the form title's help icon and the panel's own Help tab.\n const headerActions = (\n <div className=\"flex items-center gap-2\">\n <ListToolbar\n onRefresh={() => refetch()}\n isFetching={isFetching}\n apiPath={`/escalations/${esc.id}`}\n standalone\n />\n <button\n onClick={() => setSidePanelOpen((prev) => { savePanelOpen(!prev); return !prev; })}\n className=\"ml-2 text-accent/60 hover:text-accent transition-colors\"\n title={sidePanelOpen ? 'Hide side panel' : 'Show side panel'}\n >\n {sidePanelOpen\n ? <PanelRightClose className=\"w-5 h-5\" strokeWidth={1.5} />\n : <PanelRightOpen className=\"w-5 h-5\" strokeWidth={1.5} />}\n </button>\n </div>\n );\n\n return (\n // Two fixed-height columns, like the left nav: the form column scrolls\n // independently and the side panel keeps its own scroll. Negative margins\n // let the panel span the full middle row (header to event feed, flush\n // right); the left column re-adds those gutters for its own content.\n <div className=\"flex-1 min-h-0 min-w-0 flex items-stretch -mt-8 -mr-page-x -mb-16\">\n <div className=\"flex-1 min-w-0 flex flex-col min-h-0\">\n {isIframeMode ? (\n // Full-bleed iframe mode: no header, no padding, -ml-page-x cancels the shell gutter.\n // Release, Cancel, and panel toggle float at top-right over the iframe.\n <div className=\"relative flex-1 min-h-0 -ml-page-x\">\n <div className=\"absolute top-3 right-3 z-50 flex items-center gap-0.5 bg-surface/90 backdrop-blur-sm rounded-lg px-1.5 py-1 shadow-sm border border-surface-border/40\">\n {[\n {\n onClick: handleRelease,\n disabled: claim.isPending,\n label: footerLabels.release ?? 'Release',\n hoverClass: 'hover:text-accent',\n icon: <RotateCcw className=\"w-4 h-4\" strokeWidth={1.5} />,\n },\n ...(footerLabels.cancel === false ? [] : [{\n onClick: () => setCancelModalOpen(true),\n disabled: false,\n label: footerLabels.cancel ?? 'Cancel',\n hoverClass: 'hover:text-status-error',\n icon: <X className=\"w-4 h-4\" strokeWidth={1.5} />,\n }]),\n {\n onClick: () => setSidePanelOpen((prev) => { savePanelOpen(!prev); return !prev; }),\n disabled: false,\n label: sidePanelOpen ? 'Hide panel' : 'Show panel',\n hoverClass: 'hover:text-accent',\n icon: sidePanelOpen\n ? <PanelRightClose className=\"w-4 h-4\" strokeWidth={1.5} />\n : <PanelRightOpen className=\"w-4 h-4\" strokeWidth={1.5} />,\n },\n ].map(({ onClick, disabled, label, hoverClass, icon }) => (\n <div key={label} className=\"relative group/tip\">\n <button\n onClick={onClick}\n disabled={disabled}\n className={`text-text-tertiary ${hoverClass} transition-colors disabled:opacity-40 disabled:cursor-default p-1.5 rounded-md`}\n >\n {icon}\n </button>\n <span className=\"pointer-events-none absolute top-full left-1/2 -translate-x-1/2 mt-1.5 px-2 py-0.5 rounded text-2xs bg-surface-sunken text-text-primary whitespace-nowrap opacity-0 group-hover/tip:opacity-100 transition-opacity\">\n {label}\n </span>\n </div>\n ))}\n </div>\n <IframeViewport\n src={expandViewportSrc(iframeViewport!.src, esc)}\n escalation={esc}\n schema={effectiveSchema!}\n stage={VIEWPORT_STAGES.CLAIMED}\n onResolve={handleResolve}\n onEscalate={handleEscalate}\n submitAttempted={submitAttempted}\n fill\n />\n </div>\n ) : (\n <>\n {/* The description IS the title — the page opens with what to do. It\n shares the row with the toolbar and panel toggle, truncating to\n make room. */}\n <div className=\"shrink-0 pt-8 pr-page-x\">\n <PageHeader title={esc.description || 'Escalation'} actions={headerActions} />\n </div>\n\n {/* Independently scrolling form column */}\n <div className=\"flex-1 min-h-0 overflow-y-auto pr-page-x\">\n <EscalationContextBlocks\n isRoundsExhausted={isRoundsExhausted}\n payloadObj={payloadObj}\n isTerminal={isTerminal}\n resolverPayload={resolverPayload as Record<string, unknown> | null}\n onRetryTriage={handleRetryTriage}\n isRetrying={claim.isPending || resolve.isPending}\n />\n\n <EscalationFormSection\n esc={esc}\n resolverPayload={resolverPayload}\n isTerminal={isTerminal}\n editable={editable}\n activeView={activeView}\n lookup={lookupCtx}\n metadataFormSchema={metadataFormSchema}\n effectiveSchema={effectiveSchema as Record<string, unknown> | null}\n json={json}\n onJsonChange={setJson}\n requestTriage={requestTriage}\n onRequestTriageChange={setRequestTriage}\n triageNotes={triageNotes}\n onTriageNotesChange={setTriageNotes}\n onResolve={handleResolve}\n onEscalate={handleEscalate}\n onClaim={() => handleClaim(30)}\n submitAttempted={submitAttempted}\n isCertified={isCertified}\n hasAI={hasAI}\n onOpenHelp={() => {\n setSidePanelOpen(true);\n savePanelOpen(true);\n setPanelActiveView(ESCALATION_PANEL_VIEWS.HELP);\n }}\n onDisabledClick={() => setClaimNudge((n) => n + 1)}\n disabledHint={lockedFormHint}\n />\n\n <div className=\"h-10\" />\n </div>\n </>\n )}\n\n {!isIframeMode && <EscalationActionBar\n escalationContext={buildShowIfContext(esc, lookupCtx)}\n mode={actionBarMode}\n activeView={activeView}\n onActiveViewChange={setActiveView}\n onClaim={submitOnClaim ? handleClaimAndSubmit : handleClaim}\n claimPending={claim.isPending || (submitOnClaim && resolve.isPending)}\n claimNudge={claimNudge}\n workflowType={esc.workflow_type}\n json={json}\n onResolve={handleResolve}\n resolvePending={resolve.isPending || resolve.isSuccess}\n resolveError={resolve.error as Error | null}\n requestTriage={requestTriage}\n triageNotes={triageNotes}\n onRelease={handleRelease}\n releasePending={claim.isPending}\n onCancel={() => setCancelModalOpen(true)}\n assignedTo={esc.assigned_to}\n assignedUntil={esc.assigned_until}\n actingName={acting && claimedByMe ? acting.displayName : null}\n submitNeedsBadge={writeNeedsBadge}\n onSubmitAttempt={() => setSubmitAttempted(true)}\n onValidationErrors={(errors) => {\n setFormErrors(errors);\n setSidePanelOpen(true);\n savePanelOpen(true);\n setPanelActiveView(ESCALATION_PANEL_VIEWS.ERRORS);\n }}\n labels={footerLabels}\n submitBlocked={submitGuard.blocked}\n submitBlockedMessage={submitGuard.message}\n canManage={canManage}\n onReassign={() => setReassignModalOpen(true)}\n onUnassign={() => bulkUnassign.mutate({ ids: [id] })}\n unassignPending={bulkUnassign.isPending}\n />}\n </div>\n\n <EscalationSidePanel\n esc={esc}\n schema={effectiveSchema as Record<string, unknown> | null}\n envelope={envelopeObj}\n payload={payloadObj}\n resolver={resolverObj}\n triage={triageData ?? null}\n hasAI={hasAI}\n claimed={claimed}\n claimedByMe={claimedByMe}\n isTerminal={isTerminal}\n isBuilder={isBuilder}\n traceUrl={traceUrl}\n open={sidePanelOpen}\n noGutter={isIframeMode}\n formErrors={formErrors}\n activePanel={panelActiveView}\n onPanelChange={setPanelActiveView}\n canWriteItems={actionBarMode === 'available' || claimedByMe || canManage}\n />\n\n {/* The admin hand-off: the gesture IS reassign, so takeover is implied. */}\n <BulkAssignModal\n open={reassignModalOpen}\n onClose={() => setReassignModalOpen(false)}\n selectedCount={1}\n selectedRoles={esc ? [esc.role] : []}\n onSubmit={(targetUserId, durationMinutes) =>\n bulkAssign.mutate(\n { ids: [id], targetUserId, durationMinutes, reassign: true },\n { onSuccess: () => setReassignModalOpen(false) },\n )}\n isPending={bulkAssign.isPending}\n />\n\n <ConfirmCancelModal\n open={cancelModalOpen}\n onClose={() => setCancelModalOpen(false)}\n onConfirm={handleConfirmCancel}\n isPending={cancel.isPending}\n error={cancel.error as Error | null}\n actionLabel={typeof footerLabels.cancel === 'string' ? footerLabels.cancel : undefined}\n />\n\n {/* Scan hand-off: a scanned confirm-step lands here with the pending\n action in route state; the modal asks the rule's prompt. */}\n <ScanConfirmModal escalationId={id} />\n\n <StationWriteChallenge\n open={!!pendingWrite}\n verb={pendingWrite?.verb ?? 'submit'}\n claimantId={esc.assigned_to}\n wrongBadgeName={acting && acting.actorId !== esc.assigned_to ? acting.displayName : null}\n onCancel={() => setPendingWrite(null)}\n />\n\n {/* Transition hand-off: shown after a resolve on an x-lt-transition form,\n bridging the brief gap until the born-assigned follow-on arrives. */}\n <TransitionWaitModal\n open={!!waiting || autoStarting}\n message={waiting?.message\n ?? readTransitionConfig(effectiveSchema as Record<string, unknown> | null)?.message\n ?? 'Starting…'}\n />\n\n {claimedByMe && !isTerminal && esc.assigned_until && (\n <ClaimExpiryModal\n open={claimClock.expiringSoon && esc.assigned_until !== extendDismissedUntil}\n assignedUntil={esc.assigned_until}\n onClose={() => setExtendDismissedUntil(esc.assigned_until ?? null)}\n onExtend={handleClaim}\n isPending={claim.isPending}\n />\n )}\n </div>\n );\n}\n"],"names":["ScanConfirmModal","escalationId","location","useLocation","navigate","useNavigate","queryClient","useQueryClient","pending","_a","SCAN_PENDING_ACTION_STATE","busy","setBusy","useState","error","setError","dismissed","setDismissed","clearState","confirm","SCAN_VERBS","apiFetch","_b","_c","err","jsx","Modal","jsxs","COLORS","BAR_H","Marker","pct","place","label","title","emphasis","tick","text","Fragment","EscalationTimeline","esc","className","created","claimedAt","isResolved","isCancelled","isTerminal","activeClaim","isEffectivelyClaimed","showClaimed","end","total","pendingMs","activeMs","pendingPct","activePct","activeColor","activeVerb","split","formatDurationCompact","totalStr","totalTip","legend","TriageContext","triage","payload","confidence","confidenceColor","diffs","computeDiffs","action","i","diff","DiffRow","original","current","key","origVal","currVal","origStr","renderValue","corrStr","val","ACCUMULATE_ITEMS","ACCUMULATE_MAX","BATCH_ITEMS","BATCH_FILLED_AT","BATCH_KEYS","BATCH_PENDING","isRecord","v","byArrival","a","b","escalationItems","envelope","metadata","store","items","itemKey","entry","e","max","filled","stamps","keys","k","summarizePayload","parts","more","EscalationItemsPanel","canWrite","add","useAccumulateItem","remove","useRemoveAccumulatedItem","setItemKey","payloadText","setPayloadText","formError","setFormError","writable","isFull","submit","parsed","noun","headline","item","index","Link","DateValue","User","UserName","X","Plus","ESCALATION_PANEL_VIEWS","PANEL_VIEW_KEY","readPanelView","validIds","MetadataList","role","entries","displayMetadataEntries","value","displayValue","roleUrl","metadataFacetUrl","globalUrl","deriveFieldLabel","ListFilter","Search","DetailsList","claimed","isBuilder","traceUrl","PanelField","StatusBadge","RolePill","CountdownTimer","CopyableId","EscalationSidePanel","schema","resolver","hasAI","claimedByMe","open","noGutter","formErrors","activePanel","onPanelChange","canWriteItems","ALL_VIEW_IDS","internalView","setInternalView","activeView","handleViewChange","useCallback","id","useEffect","helpMarkdown","useMemo","buildHelpMarkdown","defaultHelpMarkdown","handleHelpClick","anchor","href","views","HelpCircle","MarkdownRenderer","Info","Sparkles","Tags","Boxes","Layers","JsonViewer","Braces","AlertCircle","ErrorsPanel","SlidePanel","SlidePanelViews","EscalationActionBar","props","mode","onActiveViewChange","onClaim","claimPending","claimNudge","workflowType","json","onResolve","resolvePending","resolveError","requestTriage","triageNotes","onRelease","releasePending","onCancel","assignedTo","assignedUntil","onSubmitAttempt","onValidationErrors","escalationContext","labels","submitBlocked","submitBlockedMessage","actingName","submitNeedsBadge","canManage","onReassign","onUnassign","unassignPending","claimDurations","useClaimDurations","duration","setDuration","customMinutes","setCustomMinutes","parseError","setParseError","isCustom","onCustomChange","m","guardBlocksSubmit","handleClaim","minutes","handleSubmitResolve","result","buildResolverPayload","tabClass","active","opt","CustomDurationPicker","IdCard","SectionLabel","children","RoundsExhaustedContext","resolverPayload","onRetryTriage","isRetrying","summary","diagnosis","toolCalls","ltMeta","wasTriaged","triageWorkflowId","isResolvedByTriage","showResult","setShowResult","SimpleMarkdown","VIEWPORT_STAGES","readViewport","vp","viewportSrcForStage","viewport","stage","expandViewportSrc","src","parse","s","merged","_","safeParse","IframeViewport","escalation","onEscalate","readOnly","submitAttempted","fill","iframeRef","useRef","iframeOrigin","url","sendInit","iframe","message","handler","event","data","ResolverSection","onJsonChange","onRequestTriageChange","onTriageNotesChange","disabled","showTriage","onOpenHelp","onDisabledClick","disabledHint","ResolverForm","parseJson","buildShowIfContext","lookup","EscalationContextBlocks","isRoundsExhausted","payloadObj","KeyRound","EscalationFormSection","editable","metadataFormSchema","effectiveSchema","isCertified","showIfCtx","resolvedSrc","formSchema","mapPayloadToForm","isIframeViewport","pendingSrc","MonitorPlay","ClaimExpiryModal","onClose","onExtend","isPending","claimDuration","setClaimDuration","effectiveMinutes","CLAIM_WARNING_MS","useClaimClock","warningMs","forceTick","remaining","t","timers","untilWarning","msRemaining","DRAFT_KEY_PREFIX","draftKey","readDraft","saveDraft","clearDraft","X_LT_TRANSITION","X_LT_TRANSITION_MESSAGE","X_LT_TRANSITION_MAX_WAIT_SECONDS","X_LT_TRANSITION_DONE","DEFAULT_MESSAGE","DEFAULT_MAX_WAIT_SECONDS","MIN_MAX_WAIT_SECONDS","MAX_MAX_WAIT_SECONDS","clampWaitSeconds","n","readTransitionConfig","rawMessage","readTransitionDone","raw","X_LT_SUBMIT_ON_CLAIM","readSubmitOnClaim","X_LT_SUBMIT_GUARD","SUBMIT_GUARD_DEFAULT_MESSAGE","readSubmitGuard","guard","useSubmitGuard","ctx","user","useAuth","scoped","resolveScopedQuery","isSuccess","useEscalations","count","confirmedEmpty","blocked","template","interpolateHelp","TransitionWaitModal","createPortal","StationWriteChallenge","verb","claimantId","wrongBadgeName","ScanBarcode","PANEL_OPEN_KEY","readPanelOpen","savePanelOpen","EscalationDetailPage","useParams","EscalationDetailView","isSuperAdmin","hasRoleType","acting","clearActing","useActingIdentity","scanEnabled","useScanEnabled","myRoles","useMyRoles","effectiveActorId","isLoading","refetch","isFetching","useEscalation","useEscalationDetailEvents","hasLookupRefs","lookupData","useEscalationLookups","lookupCtx","l","claim","useClaimEscalation","resolve","useResolveEscalation","escalate","useEscalateToRole","cancel","useCancelEscalation","bulkAssign","useBulkAssignEscalations","bulkUnassign","useBulkUnassignEscalations","cancelModalOpen","setCancelModalOpen","reassignModalOpen","setReassignModalOpen","workflowConfigs","useWorkflowConfigs","settings","useSettings","useAccess","wfConfig","c","setActiveView","setJson","sidePanelOpen","setSidePanelOpen","setRequestTriage","setTriageNotes","setSubmitAttempted","setFormErrors","setClaimNudge","panelActiveView","setPanelActiveView","waiting","setWaiting","autoStarting","setAutoStarting","autoStartFiredRef","autoStartActionRef","autoResolveFiredRef","autoResolveActionRef","pendingWrite","setPendingWrite","claimClock","extendDismissedUntil","setExtendDismissedUntil","validateResolverForm","resolverSchema","submitOnClaim","footerLabels","readFooterLabels","submitGuardDef","submitGuard","jsonInitialized","initialJsonRef","seeded","formDefaults","mergedPrefill","seedFormJson","draft","draftObj","timer","params","res","child","dest","run","claimedByOther","stationWorkable","isReadOnlyLogin","writeNeedsBadge","iframeViewport","isIframeMode","escalationPayload","envelopeObj","aiOverride","getAiOverride","_d","triageData","resolverObj","actionBarMode","lockedFormHint","durationMinutes","goBack","resolveDoneDest","tmpl","navigateDone","submitResolve","ApiError","isValidationErrorBody","transition","doneDest","guardStationWrite","handleResolve","handleClaimAndSubmit","dm","handleEscalate","targetRole","handleRetryTriage","handleRelease","handleConfirmCancel","headerActions","ListToolbar","prev","PanelRightClose","PanelRightOpen","RotateCcw","onClick","hoverClass","icon","PageHeader","errors","BulkAssignModal","targetUserId","ConfirmCancelModal","_e"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as d,j as s}from"./vendor-query-DvWWuNIB.js";import{h as F,i as C,g as u}from"./index-3gbd5neL.js";import{u as M,a as T,b as O,S as $,c as A,d as D,e as R}from"./StreamMessageDetail-yzWX0wac.js";import{u as z}from"./useFilterParams-DBRw1Hcv.js";import{u as E}from"./useNamespace-D1218GQ8.js";import{b as L}from"./api-path-u2Jf9QXS.js";import{D as I}from"./DataTable-spX1dFyb.js";import{S as U}from"./StickyPagination-CkW_A507.js";import{F as q,b as x,a as _}from"./FilterBar-C-iur-Nj.js";import{P as B}from"./PageHeader-Csq434-v.js";import{L as V}from"./ListToolbar-h_ziBt4O.js";import{X as H,a2 as J}from"./vendor-icons-B7OcUM6b.js";import"./vendor-react-_9U4mKFr.js";import"./controlplane-CyfwUlFA.js";import"./EmptyState-DgC_6njU.js";import"./useContainerWidth-DSLp1gEm.js";function W(l,n){if(l.length<=n)return l;const a=Math.floor((n-1)/2);return`${l.slice(0,a)}…${l.slice(-a)}`}function me(){const{namespace:l,available:n}=E("namespace"),{filters:a,setFilter:t,pagination:r,sort:c,setSort:h}=z({filters:{namespace:"",source:"worker",status:"",stream_name:"",msg_type:"",topic:"",workflow_name:"",jid:"",aid:""}}),[m,g]=d.useState(null),v=d.useMemo(()=>n.map(e=>({value:e,label:e})),[n]),{data:i,isLoading:b,refetch:j,isFetching:w}=M({namespace:a.namespace||l,source:a.source||"worker",limit:r.pageSize,offset:r.offset,sort_by:c.sort_by||"created_at",order:c.order||"desc",status:a.status||void 0,stream_name:a.stream_name||void 0,msg_type:a.msg_type||void 0,topic:a.topic||void 0,workflow_name:a.workflow_name||void 0,jid:a.jid||void 0,aid:a.aid||void 0}),p=(i==null?void 0:i.messages)??[],f=(i==null?void 0:i.total)??0,o=d.useMemo(()=>m?p.find(e=>e.id===m.id&&e.source===m.source)??m:null,[p,m]),S=!!o,[k,N]=d.useState(null);d.useEffect(()=>{o&&N(o)},[o]);const y=o??k,P=[{key:"stream_name",label:"Stream",sortable:!0,render:e=>s.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[s.jsx("span",{className:`w-2.5 h-2.5 shrink-0 rounded-full dot-ring ${D[e.status]}`,title:A[e.status]}),s.jsx("span",{className:"font-mono text-xs text-text-secondary truncate",title:e.stream_name,children:e.stream_name})]})},{key:"source",label:"Source",render:e=>s.jsx("span",{className:R,children:e.source}),className:"w-24"},{key:"created_at",label:"Created",sortable:!0,render:e=>s.jsx(u,{date:e.created_at,format:"relative",className:"text-xs text-text-secondary whitespace-nowrap"}),className:"w-32"},{key:"reserved_at",label:"Reserved",render:e=>e.reserved_at?s.jsx(u,{date:e.reserved_at,format:"relative",className:"text-xs text-text-secondary whitespace-nowrap"}):s.jsx("span",{className:"text-xs text-text-tertiary",children:"—"}),className:"w-32"},{key:"expired_at",label:"Processed",render:e=>e.expired_at?s.jsx(u,{date:e.expired_at,format:"relative",className:"text-xs text-text-secondary whitespace-nowrap"}):s.jsx("span",{className:"text-xs text-text-tertiary",children:"—"}),className:"w-32"},{key:"priority",label:"Pri",sortable:!0,render:e=>s.jsx("span",{className:"text-xs text-text-secondary",children:e.priority}),className:"w-14 text-right"},{key:"retry_attempt",label:"Retries",render:e=>s.jsxs("span",{className:"text-xs text-text-secondary whitespace-nowrap",children:[e.retry_attempt,"/",e.max_retry_attempts]}),className:"w-20"}];return s.jsxs("div",{className:"flex items-stretch min-w-0 -mt-10 -mr-10 -mb-16",children:[s.jsxs("div",{className:"flex-1 min-w-0 pt-10 pr-10",children:[s.jsx(B,{title:"Messages",docsHash:"#docs:dashboard.md:messages"}),s.jsxs(q,{actions:s.jsx(V,{onRefresh:()=>j(),isFetching:w,apiPath:L("/controlplane/stream-messages",{namespace:a.namespace||l,source:a.source||"worker",limit:r.pageSize,offset:r.offset,sort_by:c.sort_by||"created_at",order:c.order||"desc",status:a.status||void 0,stream_name:a.stream_name||void 0,msg_type:a.msg_type||void 0,topic:a.topic||void 0,workflow_name:a.workflow_name||void 0,jid:a.jid||void 0,aid:a.aid||void 0})}),children:[s.jsx(x,{label:"Namespace",value:a.namespace,onChange:e=>t("namespace",e),options:v,required:!0}),s.jsx(x,{label:"Source",value:a.source,onChange:e=>t("source",e),options:T,required:!0}),s.jsx(x,{label:"Status",value:a.status,onChange:e=>t("status",e),options:O}),s.jsx(_,{label:"Stream",value:a.stream_name,onChange:e=>t("stream_name",e),placeholder:"Filter by stream name…"}),s.jsx(_,{label:"Job ID",value:a.jid,onChange:e=>t("jid",e),placeholder:"Filter by jid…"}),[{key:"topic",label:"Topic",value:a.topic},{key:"workflow_name",label:"Workflow",value:a.workflow_name},{key:"aid",label:"Activity",value:a.aid},{key:"msg_type",label:"Type",value:a.msg_type}].filter(e=>e.value).map(e=>s.jsxs("button",{onClick:()=>t(e.key,""),className:"inline-flex items-center gap-1 px-2 py-0.5 text-2xs font-mono rounded-full bg-accent/15 text-accent hover:bg-accent/25 transition-colors",title:`Clear ${e.label} filter: ${e.value}`,children:[e.label,": ",W(e.value,12),s.jsx(H,{className:"w-2.5 h-2.5"})]},e.key))]}),s.jsx(I,{columns:P,data:p,keyFn:e=>`${e.source}:${e.id}`,isLoading:b,emptyMessage:"No stream messages found",onRowClick:e=>g(e),activeRowKey:o?`${o.source}:${o.id}`:null,sort:c,onSort:h,layout:"fixed"}),s.jsx(U,{page:r.page,totalPages:r.totalPages(f),onPageChange:r.setPage,total:f,pageSize:r.pageSize,onPageSizeChange:r.setPageSize})]}),s.jsx(F,{open:S,width:416,className:"self-stretch",children:s.jsx("div",{className:"h-full pl-4",children:s.jsx(C,{views:[{id:"detail",icon:J,label:"Message Detail",content:y?s.jsx($,{message:y,filters:{onFilterStatus:e=>t("status",e),onFilterStreamName:e=>t("stream_name",e),onFilterMsgType:e=>t("msg_type",e),onFilterTopic:e=>t("topic",e),onFilterWorkflow:e=>t("workflow_name",e),onFilterJid:e=>t("jid",e),onFilterAid:e=>t("aid",e)}}):null}],activeId:"detail",onViewChange:()=>{},onClose:()=>g(null),stickyClassName:"sticky top-0 z-10 h-[calc(100vh-5.25rem)] pt-9"})})})]})}export{me as StreamMessagesPage};
|
|
2
|
+
//# sourceMappingURL=index-CVwzCZ43.js.map
|