@burdenoff/microfe-botmanor 2026.1027.2 → 2026.1028.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/botlit/BotlitRoutes.js +142 -132
  2. package/dist/botlit/BotlitRoutes.js.map +1 -1
  3. package/dist/botlit/components/AgentApprovalPanel.js +144 -126
  4. package/dist/botlit/components/AgentApprovalPanel.js.map +1 -1
  5. package/dist/botlit/components/ModelRegistrationPanel.js +4 -4
  6. package/dist/botlit/components/RetentionStatusPanel.js +95 -0
  7. package/dist/botlit/components/RetentionStatusPanel.js.map +1 -0
  8. package/dist/botlit/components/VersionHistoryPanel.js +92 -83
  9. package/dist/botlit/components/VersionHistoryPanel.js.map +1 -1
  10. package/dist/botlit/hooks/index.js +1 -0
  11. package/dist/botlit/hooks/useRetention.js +31 -0
  12. package/dist/botlit/hooks/useRetention.js.map +1 -0
  13. package/dist/botlit/pages/A2AAgentDetailPage.js +212 -203
  14. package/dist/botlit/pages/A2AAgentDetailPage.js.map +1 -1
  15. package/dist/botlit/pages/A2ARegistryPage.js +129 -117
  16. package/dist/botlit/pages/A2ARegistryPage.js.map +1 -1
  17. package/dist/botlit/pages/AgentApprovalsPage.js +122 -110
  18. package/dist/botlit/pages/AgentApprovalsPage.js.map +1 -1
  19. package/dist/botlit/pages/AgentBuilderPage.js +18 -18
  20. package/dist/botlit/pages/AgentDetailPage.js +298 -273
  21. package/dist/botlit/pages/AgentDetailPage.js.map +1 -1
  22. package/dist/botlit/pages/AgentsListPage.js +145 -121
  23. package/dist/botlit/pages/AgentsListPage.js.map +1 -1
  24. package/dist/botlit/pages/BotChannelsTab.js +238 -226
  25. package/dist/botlit/pages/BotChannelsTab.js.map +1 -1
  26. package/dist/botlit/pages/BotDetailPage.js +307 -288
  27. package/dist/botlit/pages/BotDetailPage.js.map +1 -1
  28. package/dist/botlit/pages/BotWorkflowTab.js +125 -116
  29. package/dist/botlit/pages/BotWorkflowTab.js.map +1 -1
  30. package/dist/botlit/pages/BotsListPage.js +159 -144
  31. package/dist/botlit/pages/BotsListPage.js.map +1 -1
  32. package/dist/botlit/pages/BucketsPage.js +279 -258
  33. package/dist/botlit/pages/BucketsPage.js.map +1 -1
  34. package/dist/botlit/pages/ChannelDetailPage.js +346 -328
  35. package/dist/botlit/pages/ChannelDetailPage.js.map +1 -1
  36. package/dist/botlit/pages/ChannelRoutesPage.js +10 -10
  37. package/dist/botlit/pages/ChannelsPage.js +163 -148
  38. package/dist/botlit/pages/ChannelsPage.js.map +1 -1
  39. package/dist/botlit/pages/DashboardPage.js +129 -114
  40. package/dist/botlit/pages/DashboardPage.js.map +1 -1
  41. package/dist/botlit/pages/ExecutionDetailPage.js +145 -139
  42. package/dist/botlit/pages/ExecutionDetailPage.js.map +1 -1
  43. package/dist/botlit/pages/ExecutionsPage.js +14 -14
  44. package/dist/botlit/pages/InstalledPackagesPage.js +245 -227
  45. package/dist/botlit/pages/InstalledPackagesPage.js.map +1 -1
  46. package/dist/botlit/pages/KnowledgeBaseDetailPage.js +168 -156
  47. package/dist/botlit/pages/KnowledgeBaseDetailPage.js.map +1 -1
  48. package/dist/botlit/pages/KnowledgeBasesPage.js +91 -79
  49. package/dist/botlit/pages/KnowledgeBasesPage.js.map +1 -1
  50. package/dist/botlit/pages/MCPServerDetailPage.js +200 -188
  51. package/dist/botlit/pages/MCPServerDetailPage.js.map +1 -1
  52. package/dist/botlit/pages/MCPServersPage.js +170 -155
  53. package/dist/botlit/pages/MCPServersPage.js.map +1 -1
  54. package/dist/botlit/pages/PoliciesPage.js +115 -104
  55. package/dist/botlit/pages/PoliciesPage.js.map +1 -1
  56. package/dist/botlit/pages/PolicyDetailPage.js +368 -324
  57. package/dist/botlit/pages/PolicyDetailPage.js.map +1 -1
  58. package/dist/botlit/pages/PromptStudioPage.js +106 -97
  59. package/dist/botlit/pages/PromptStudioPage.js.map +1 -1
  60. package/dist/botlit/pages/PromptTemplateDetailPage.js +359 -347
  61. package/dist/botlit/pages/PromptTemplateDetailPage.js.map +1 -1
  62. package/dist/botlit/pages/PromptTemplatesPage.js +127 -112
  63. package/dist/botlit/pages/PromptTemplatesPage.js.map +1 -1
  64. package/dist/botlit/pages/RetrievalProfileDetailPage.js +219 -210
  65. package/dist/botlit/pages/RetrievalProfileDetailPage.js.map +1 -1
  66. package/dist/botlit/pages/RetrievalProfilesPage.js +112 -103
  67. package/dist/botlit/pages/RetrievalProfilesPage.js.map +1 -1
  68. package/dist/botlit/pages/SettingsPage.js +264 -252
  69. package/dist/botlit/pages/SettingsPage.js.map +1 -1
  70. package/dist/botlit/pages/SkillDetailPage.js +235 -226
  71. package/dist/botlit/pages/SkillDetailPage.js.map +1 -1
  72. package/dist/botlit/pages/SkillsPage.js +115 -106
  73. package/dist/botlit/pages/SkillsPage.js.map +1 -1
  74. package/dist/botlit/pages/StorePackageDetailPage.js +194 -188
  75. package/dist/botlit/pages/StorePackageDetailPage.js.map +1 -1
  76. package/dist/botlit/pages/StorePage.js +161 -155
  77. package/dist/botlit/pages/StorePage.js.map +1 -1
  78. package/dist/botlit/permissions/PermissionRequired.js +45 -0
  79. package/dist/botlit/permissions/PermissionRequired.js.map +1 -0
  80. package/dist/botlit/permissions/botmanorPermissions.js +72 -0
  81. package/dist/botlit/permissions/botmanorPermissions.js.map +1 -0
  82. package/dist/botlit/permissions/index.js +3 -0
  83. package/dist/botlit/permissions/useBotmanorPermissions.js +19 -0
  84. package/dist/botlit/permissions/useBotmanorPermissions.js.map +1 -0
  85. package/dist/botlit/utils/policyConfig.js +67 -42
  86. package/dist/botlit/utils/policyConfig.js.map +1 -1
  87. package/dist/generated/global-types.js.map +1 -1
  88. package/dist/generated/wspace-operations.js +90 -54
  89. package/dist/generated/wspace-operations.js.map +1 -1
  90. package/dist/generated/wspace-types.js.map +1 -1
  91. package/package.json +1 -1
@@ -0,0 +1,45 @@
1
+ import { useTr as e } from "../../shared/hooks/useTr.js";
2
+ import { useBotlit as t } from "../providers/BotlitProvider.js";
3
+ import { BotmanorPermissionGate as n } from "./useBotmanorPermissions.js";
4
+ import { PageLayout as r } from "../components/PageLayout.js";
5
+ import { Button as i } from "../components/Button.js";
6
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
7
+ import { Lock as s } from "lucide-react";
8
+ //#region src/botlit/permissions/PermissionRequired.tsx
9
+ var c = ({ title: n, backTo: c, backLabel: l }) => {
10
+ let u = e(), { navigateTo: d } = t();
11
+ return /* @__PURE__ */ a(r, {
12
+ title: n,
13
+ children: /* @__PURE__ */ o("div", {
14
+ role: "status",
15
+ className: "flex flex-col items-center justify-center gap-3 rounded-lg border border-border-subtle bg-bg-surface px-4 py-12 text-center",
16
+ children: [
17
+ /* @__PURE__ */ a(s, {
18
+ size: 28,
19
+ className: "text-text-secondary"
20
+ }),
21
+ /* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("h2", {
22
+ className: "text-lg font-medium text-text-primary",
23
+ children: u("permissions.required.title", "You do not have access to this action")
24
+ }), /* @__PURE__ */ a("p", {
25
+ className: "mt-2 max-w-md text-sm text-text-secondary",
26
+ children: u("permissions.required.description", "Your role in this workspace can view BotManor resources but not change them. Ask a workspace admin for edit access.")
27
+ })] }),
28
+ /* @__PURE__ */ a(i, {
29
+ variant: "outline",
30
+ size: "sm",
31
+ onClick: () => d(c),
32
+ children: l
33
+ })
34
+ ]
35
+ })
36
+ });
37
+ }, l = ({ permission: e, children: t, ...r }) => /* @__PURE__ */ a(n, {
38
+ permission: e,
39
+ fallback: /* @__PURE__ */ a(c, { ...r }),
40
+ children: t
41
+ });
42
+ //#endregion
43
+ export { c as PermissionRequiredState, l as RequirePermission };
44
+
45
+ //# sourceMappingURL=PermissionRequired.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PermissionRequired.js","names":[],"sources":["../../../src/botlit/permissions/PermissionRequired.tsx"],"sourcesContent":["/**\n * Permission-required state\n * @module botlit/permissions\n *\n * Shown in place of a create form when a member opens a `/new` route (from a\n * bookmark or a pasted link) without the permission to create that resource.\n * The member still sees where they are and how to get back; they are not shown\n * a form whose submit the gateway would refuse.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { Lock } from 'lucide-react';\n\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { PageLayout } from '../components/PageLayout';\nimport { Button } from '../components/Button';\nimport type { BotmanorPermission } from './botmanorPermissions';\nimport { BotmanorPermissionGate } from './useBotmanorPermissions';\n\nexport interface PermissionRequiredStateProps {\n /** Page title, e.g. \"Create Policy\". */\n title: string;\n /** Where \"Back\" goes, relative to the BotManor mount (e.g. `policies`). */\n backTo: string;\n /** Label for the back link, e.g. \"Back to policies\". */\n backLabel: string;\n}\n\nexport const PermissionRequiredState: FC<PermissionRequiredStateProps> = ({\n title,\n backTo,\n backLabel,\n}) => {\n const t = useTr();\n const { navigateTo } = useBotlit();\n return (\n <PageLayout title={title}>\n <div\n role=\"status\"\n className=\"flex flex-col items-center justify-center gap-3 rounded-lg border border-border-subtle bg-bg-surface px-4 py-12 text-center\"\n >\n <Lock size={28} className=\"text-text-secondary\" />\n <div>\n <h2 className=\"text-lg font-medium text-text-primary\">\n {t('permissions.required.title', 'You do not have access to this action')}\n </h2>\n <p className=\"mt-2 max-w-md text-sm text-text-secondary\">\n {t(\n 'permissions.required.description',\n 'Your role in this workspace can view BotManor resources but not change them. Ask a workspace admin for edit access.'\n )}\n </p>\n </div>\n <Button variant=\"outline\" size=\"sm\" onClick={() => navigateTo(backTo)}>\n {backLabel}\n </Button>\n </div>\n </PageLayout>\n );\n};\n\nexport interface RequirePermissionProps extends PermissionRequiredStateProps {\n permission: BotmanorPermission;\n children: ReactNode;\n}\n\n/**\n * Render a create page only for members who may create; everyone else gets the\n * permission-required state. Nothing renders while permissions load.\n */\nexport const RequirePermission: FC<RequirePermissionProps> = ({\n permission,\n children,\n ...state\n}) => (\n <BotmanorPermissionGate permission={permission} fallback={<PermissionRequiredState {...state} />}>\n {children}\n </BotmanorPermissionGate>\n);\n"],"mappings":";;;;;;;;AA6BA,IAAa,KAA6D,EACxE,UACA,WACA,mBACI;CACJ,IAAM,IAAI,GAAO,EACX,EAAE,kBAAe,GAAW;AAClC,QACE,kBAAC,GAAD;EAAmB;YACjB,kBAAC,OAAD;GACE,MAAK;GACL,WAAU;aAFZ;IAIE,kBAAC,GAAD;KAAM,MAAM;KAAI,WAAU;KAAwB,CAAA;IAClD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAE,8BAA8B,wCAAwC;KACtE,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,oCACA,sHACD;KACC,CAAA,CACA,EAAA,CAAA;IACN,kBAAC,GAAD;KAAQ,SAAQ;KAAU,MAAK;KAAK,eAAe,EAAW,EAAO;eAClE;KACM,CAAA;IACL;;EACK,CAAA;GAaJ,KAAiD,EAC5D,eACA,aACA,GAAG,QAEH,kBAAC,GAAD;CAAoC;CAAY,UAAU,kBAAC,GAAD,EAAyB,GAAI,GAAS,CAAA;CAC7F;CACsB,CAAA"}
@@ -0,0 +1,72 @@
1
+ //#region src/botlit/permissions/botmanorPermissions.ts
2
+ var e = {
3
+ APP_CREATE: "botmanor-app:create",
4
+ APP_UPDATE: "botmanor-app:update",
5
+ APP_DELETE: "botmanor-app:delete",
6
+ APP_PUBLISH: "botmanor-app:publish",
7
+ APP_ROLLBACK: "botmanor-app:rollback",
8
+ APP_AGENT_CREATE: "botmanor-app-agent:create",
9
+ APP_AGENT_UPDATE: "botmanor-app-agent:update",
10
+ APP_AGENT_DELETE: "botmanor-app-agent:delete",
11
+ AGENT_CREATE: "botmanor-agent:create",
12
+ AGENT_UPDATE: "botmanor-agent:update",
13
+ AGENT_DELETE: "botmanor-agent:delete",
14
+ AGENT_PUBLISH: "botmanor-agent:publish",
15
+ AGENT_ROLLBACK: "botmanor-agent:rollback",
16
+ AGENT_EXECUTE: "botmanor-agent:execute",
17
+ AGENT_APPROVE: "botmanor-agent:approve",
18
+ KNOWLEDGE_CREATE: "botmanor-knowledge-base:create",
19
+ KNOWLEDGE_UPDATE: "botmanor-knowledge-base:update",
20
+ KNOWLEDGE_DELETE: "botmanor-knowledge-base:delete",
21
+ KNOWLEDGE_MANAGE: "botmanor-knowledge-base:manage",
22
+ RETRIEVAL_PROFILE_CREATE: "botmanor-retrieval-profile:create",
23
+ RETRIEVAL_PROFILE_UPDATE: "botmanor-retrieval-profile:update",
24
+ RETRIEVAL_PROFILE_DELETE: "botmanor-retrieval-profile:delete",
25
+ MCP_CREATE: "botmanor-mcp:create",
26
+ MCP_UPDATE: "botmanor-mcp:update",
27
+ MCP_DELETE: "botmanor-mcp:delete",
28
+ MCP_TEST: "botmanor-mcp:test",
29
+ MCP_EXECUTE: "botmanor-mcp:execute",
30
+ LLM_CREATE: "botmanor-llm:create",
31
+ LLM_UPDATE: "botmanor-llm:update",
32
+ LLM_DELETE: "botmanor-llm:delete",
33
+ LLM_TEST: "botmanor-llm:test",
34
+ A2A_CREATE: "botmanor-a2a:create",
35
+ A2A_UPDATE: "botmanor-a2a:update",
36
+ A2A_DELETE: "botmanor-a2a:delete",
37
+ A2A_SEND: "botmanor-a2a:send",
38
+ A2A_EXECUTE: "botmanor-a2a:execute",
39
+ A2A_REGISTRY_CREATE: "botmanor-a2a-registry:create",
40
+ A2A_REGISTRY_UPDATE: "botmanor-a2a-registry:update",
41
+ A2A_REGISTRY_DELETE: "botmanor-a2a-registry:delete",
42
+ CHANNEL_CREATE: "botmanor-channel:create",
43
+ CHANNEL_UPDATE: "botmanor-channel:update",
44
+ CHANNEL_DELETE: "botmanor-channel:delete",
45
+ CHANNEL_MESSAGE_CREATE: "botmanor-channel-message:create",
46
+ CHANNEL_MESSAGE_EXECUTE: "botmanor-channel-message:execute",
47
+ POLICY_CREATE: "botmanor-policy:create",
48
+ POLICY_UPDATE: "botmanor-policy:update",
49
+ POLICY_DELETE: "botmanor-policy:delete",
50
+ SKILL_CREATE: "botmanor-skill:create",
51
+ SKILL_UPDATE: "botmanor-skill:update",
52
+ SKILL_DELETE: "botmanor-skill:delete",
53
+ PROMPT_TEMPLATE_CREATE: "botmanor-prompt-template:create",
54
+ PROMPT_TEMPLATE_UPDATE: "botmanor-prompt-template:update",
55
+ PROMPT_TEMPLATE_DELETE: "botmanor-prompt-template:delete",
56
+ PROMPT_TEMPLATE_EXECUTE: "botmanor-prompt-template:execute",
57
+ EXECUTION_CREATE: "botmanor-execution:create",
58
+ EXECUTION_UPDATE: "botmanor-execution:update",
59
+ BUCKET_CREATE: "botmanor-bucket:create",
60
+ BUCKET_UPDATE: "botmanor-bucket:update",
61
+ BUCKET_DELETE: "botmanor-bucket:delete",
62
+ STORE_CREATE: "botmanor-store:create",
63
+ STORE_UPDATE: "botmanor-store:update",
64
+ STORE_DELETE: "botmanor-store:delete",
65
+ TEMPLATE_CREATE: "botmanor-template:create",
66
+ TEMPLATE_UPDATE: "botmanor-template:update",
67
+ TEMPLATE_DELETE: "botmanor-template:delete"
68
+ };
69
+ //#endregion
70
+ export { e as BOTMANOR_PERMISSIONS };
71
+
72
+ //# sourceMappingURL=botmanorPermissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"botmanorPermissions.js","names":[],"sources":["../../../src/botlit/permissions/botmanorPermissions.ts"],"sourcesContent":["/**\n * BotManor permission catalogue\n * @module botlit/permissions\n *\n * The `resource:action` pairs the BotManor UI checks before it offers a write\n * control. Every pair mirrors the `@rbac(resource, action)` directive on the\n * wspace-botmanor-svc mutation that the control calls, so the UI hides exactly\n * what the gateway would refuse. The gateway stays the only enforcement point:\n * hiding a button never grants or denies anything, it only stops a member from\n * attempting an action the gateway will answer with FORBIDDEN.\n *\n * Keep this file in step with the service schema. When a mutation's directive\n * changes, change the pair here and the control that calls it.\n */\n\nexport const BOTMANOR_PERMISSIONS = {\n // Bot apps — createBotApp / updateBotApp / deleteBotApp / publishBotAppVersion /\n // rollbackBotAppVersion\n APP_CREATE: 'botmanor-app:create',\n APP_UPDATE: 'botmanor-app:update',\n APP_DELETE: 'botmanor-app:delete',\n APP_PUBLISH: 'botmanor-app:publish',\n APP_ROLLBACK: 'botmanor-app:rollback',\n\n // Agents behind an app — createAppAgent / updateAppAgent / deleteAppAgent\n APP_AGENT_CREATE: 'botmanor-app-agent:create',\n APP_AGENT_UPDATE: 'botmanor-app-agent:update',\n APP_AGENT_DELETE: 'botmanor-app-agent:delete',\n\n // Agents — createBotmanorAgent / updateBotmanorAgent / deleteBotmanorAgent /\n // publishAgentVersion / rollbackAgentVersion / chatWithAgent /\n // approve|rejectBotmanorAgentActivation / updateBotmanorAgentApprovalSettings /\n // configureAssistantRuntime / publishAssistantWebsite / configureAssistantChannel\n AGENT_CREATE: 'botmanor-agent:create',\n AGENT_UPDATE: 'botmanor-agent:update',\n AGENT_DELETE: 'botmanor-agent:delete',\n AGENT_PUBLISH: 'botmanor-agent:publish',\n AGENT_ROLLBACK: 'botmanor-agent:rollback',\n AGENT_EXECUTE: 'botmanor-agent:execute',\n AGENT_APPROVE: 'botmanor-agent:approve',\n\n // Knowledge bases — createKnowledgeBase / addKnowledgeDocument /\n // updateKnowledgeBase / deleteKnowledgeBase / removeKnowledgeDocument /\n // rebuildKnowledgeBaseIndex / queueKnowledgeSource / retry*\n KNOWLEDGE_CREATE: 'botmanor-knowledge-base:create',\n KNOWLEDGE_UPDATE: 'botmanor-knowledge-base:update',\n KNOWLEDGE_DELETE: 'botmanor-knowledge-base:delete',\n KNOWLEDGE_MANAGE: 'botmanor-knowledge-base:manage',\n\n // Retrieval profiles\n RETRIEVAL_PROFILE_CREATE: 'botmanor-retrieval-profile:create',\n RETRIEVAL_PROFILE_UPDATE: 'botmanor-retrieval-profile:update',\n RETRIEVAL_PROFILE_DELETE: 'botmanor-retrieval-profile:delete',\n\n // MCP servers — create/update/deleteMCPConnection, updateMCPTool,\n // testMCPConnection, refreshMCPTools / refreshMCPResources / callMCPTool\n MCP_CREATE: 'botmanor-mcp:create',\n MCP_UPDATE: 'botmanor-mcp:update',\n MCP_DELETE: 'botmanor-mcp:delete',\n MCP_TEST: 'botmanor-mcp:test',\n MCP_EXECUTE: 'botmanor-mcp:execute',\n\n // LLM providers and models — create/update/delete*, testLlmConnection\n LLM_CREATE: 'botmanor-llm:create',\n LLM_UPDATE: 'botmanor-llm:update',\n LLM_DELETE: 'botmanor-llm:delete',\n LLM_TEST: 'botmanor-llm:test',\n\n // A2A — create/update/deleteA2AConnection, sendA2AMessage, delegateA2ATask\n A2A_CREATE: 'botmanor-a2a:create',\n A2A_UPDATE: 'botmanor-a2a:update',\n A2A_DELETE: 'botmanor-a2a:delete',\n A2A_SEND: 'botmanor-a2a:send',\n A2A_EXECUTE: 'botmanor-a2a:execute',\n\n // A2A registry (the external agents the Registry page lists) —\n // create/update/deleteA2ARegistryEntry\n A2A_REGISTRY_CREATE: 'botmanor-a2a-registry:create',\n A2A_REGISTRY_UPDATE: 'botmanor-a2a-registry:update',\n A2A_REGISTRY_DELETE: 'botmanor-a2a-registry:delete',\n\n // Channels — connectors and routes\n CHANNEL_CREATE: 'botmanor-channel:create',\n CHANNEL_UPDATE: 'botmanor-channel:update',\n CHANNEL_DELETE: 'botmanor-channel:delete',\n // sendChannelMessage is :create, handleChannelMessage is :execute\n CHANNEL_MESSAGE_CREATE: 'botmanor-channel-message:create',\n CHANNEL_MESSAGE_EXECUTE: 'botmanor-channel-message:execute',\n\n // Policies\n POLICY_CREATE: 'botmanor-policy:create',\n POLICY_UPDATE: 'botmanor-policy:update',\n POLICY_DELETE: 'botmanor-policy:delete',\n\n // Skills\n SKILL_CREATE: 'botmanor-skill:create',\n SKILL_UPDATE: 'botmanor-skill:update',\n SKILL_DELETE: 'botmanor-skill:delete',\n\n // Prompt templates — create/duplicate share :create; renderPromptTemplate is :execute\n PROMPT_TEMPLATE_CREATE: 'botmanor-prompt-template:create',\n PROMPT_TEMPLATE_UPDATE: 'botmanor-prompt-template:update',\n PROMPT_TEMPLATE_DELETE: 'botmanor-prompt-template:delete',\n PROMPT_TEMPLATE_EXECUTE: 'botmanor-prompt-template:execute',\n\n // Executions — createExecution / triggerBoundWorkflow are :create,\n // runExecution / updateExecutionStatus are :update\n EXECUTION_CREATE: 'botmanor-execution:create',\n EXECUTION_UPDATE: 'botmanor-execution:update',\n\n // Buckets\n BUCKET_CREATE: 'botmanor-bucket:create',\n BUCKET_UPDATE: 'botmanor-bucket:update',\n BUCKET_DELETE: 'botmanor-bucket:delete',\n\n // Store — installPackage / forkInstalledPackage are :create,\n // updateInstalledPackage :update, uninstallPackage :delete\n STORE_CREATE: 'botmanor-store:create',\n STORE_UPDATE: 'botmanor-store:update',\n STORE_DELETE: 'botmanor-store:delete',\n\n // Agent and app templates\n TEMPLATE_CREATE: 'botmanor-template:create',\n TEMPLATE_UPDATE: 'botmanor-template:update',\n TEMPLATE_DELETE: 'botmanor-template:delete',\n} as const;\n\nexport type BotmanorPermission = (typeof BOTMANOR_PERMISSIONS)[keyof typeof BOTMANOR_PERMISSIONS];\n"],"mappings":";AAeA,IAAa,IAAuB;CAGlC,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,aAAa;CACb,cAAc;CAGd,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAMlB,cAAc;CACd,cAAc;CACd,cAAc;CACd,eAAe;CACf,gBAAgB;CAChB,eAAe;CACf,eAAe;CAKf,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAGlB,0BAA0B;CAC1B,0BAA0B;CAC1B,0BAA0B;CAI1B,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,UAAU;CACV,aAAa;CAGb,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,UAAU;CAGV,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,UAAU;CACV,aAAa;CAIb,qBAAqB;CACrB,qBAAqB;CACrB,qBAAqB;CAGrB,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAEhB,wBAAwB;CACxB,yBAAyB;CAGzB,eAAe;CACf,eAAe;CACf,eAAe;CAGf,cAAc;CACd,cAAc;CACd,cAAc;CAGd,wBAAwB;CACxB,wBAAwB;CACxB,wBAAwB;CACxB,yBAAyB;CAIzB,kBAAkB;CAClB,kBAAkB;CAGlB,eAAe;CACf,eAAe;CACf,eAAe;CAIf,cAAc;CACd,cAAc;CACd,cAAc;CAGd,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CAClB"}
@@ -0,0 +1,3 @@
1
+ import "./botmanorPermissions.js";
2
+ import "./useBotmanorPermissions.js";
3
+ import "./PermissionRequired.js";
@@ -0,0 +1,19 @@
1
+ import { useCallback as e } from "react";
2
+ import { Fragment as t, jsx as n } from "react/jsx-runtime";
3
+ import { useSafePermissions as r } from "@burdenoff/fe-libs/shared/providers/shell";
4
+ //#region src/botlit/permissions/useBotmanorPermissions.tsx
5
+ function i(e, t) {
6
+ return e ? e.isLoading ? "loading" : e.hasPermission(t) ? "allowed" : "denied" : "allowed";
7
+ }
8
+ function a() {
9
+ let t = r();
10
+ return e((e) => i(t, e) === "allowed", [t]);
11
+ }
12
+ var o = ({ permission: e, children: a, fallback: o = null, loading: s = null }) => {
13
+ let c = i(r(), e);
14
+ return c === "allowed" ? /* @__PURE__ */ n(t, { children: a }) : c === "loading" ? /* @__PURE__ */ n(t, { children: s }) : /* @__PURE__ */ n(t, { children: o });
15
+ };
16
+ //#endregion
17
+ export { o as BotmanorPermissionGate, i as resolvePermission, a as useBotmanorCan };
18
+
19
+ //# sourceMappingURL=useBotmanorPermissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBotmanorPermissions.js","names":[],"sources":["../../../src/botlit/permissions/useBotmanorPermissions.tsx"],"sourcesContent":["/**\n * BotManor permission hooks and gate\n * @module botlit/permissions\n *\n * Thin wrappers over the shell's fe-libs `PermissionProvider` that decide\n * whether the UI should offer a write control. They follow the fe-libs\n * `PermissionGate` contract (render children when allowed, `fallback` when\n * denied, `loading` while the permission set is still being fetched) with one\n * difference: they read the context through `useSafePermissions`, so the MFE\n * keeps rendering in a host that has not mounted RBAC (the standalone dev\n * host, Storybook, unit tests). With no provider every control is offered,\n * which is exactly the behaviour before gating existed; the gateway's `@rbac`\n * directive remains the enforcement point either way.\n *\n * While permissions load, checks answer `false` so a viewer never sees a\n * control flash on and then disappear.\n */\n\nimport { useCallback, type FC, type ReactNode } from 'react';\nimport { useSafePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\n\nimport type { BotmanorPermission } from './botmanorPermissions';\n\n/** Answers whether the current member may use the control behind `permission`. */\nexport type BotmanorPermissionCheck = (permission: BotmanorPermission) => boolean;\n\n/** Outcome of resolving a permission against the shell context. */\nexport type PermissionDecision = 'allowed' | 'denied' | 'loading';\n\n/**\n * Resolve one permission against a (possibly absent) permission context.\n * Exported for unit tests; components use the hooks below.\n */\nexport function resolvePermission(\n context: ReturnType<typeof useSafePermissions>,\n permission: BotmanorPermission\n): PermissionDecision {\n if (!context) return 'allowed';\n if (context.isLoading) return 'loading';\n return context.hasPermission(permission) ? 'allowed' : 'denied';\n}\n\n/**\n * Returns a stable checker for any number of permissions on one page.\n *\n * @example\n * const can = useBotmanorCan();\n * {can(BOTMANOR_PERMISSIONS.POLICY_DELETE) && <Button onClick={remove}>Delete</Button>}\n */\nexport function useBotmanorCan(): BotmanorPermissionCheck {\n const context = useSafePermissions();\n return useCallback(\n (permission: BotmanorPermission) => resolvePermission(context, permission) === 'allowed',\n [context]\n );\n}\n\n/** Single-permission form of {@link useBotmanorCan}. */\nexport function useBotmanorPermission(permission: BotmanorPermission): boolean {\n return useBotmanorCan()(permission);\n}\n\nexport interface BotmanorPermissionGateProps {\n /** The `resource:action` pair the wrapped control needs. */\n permission: BotmanorPermission;\n /** Rendered when the member has the permission. */\n children: ReactNode;\n /** Rendered when the member lacks the permission. Nothing by default. */\n fallback?: ReactNode;\n /** Rendered while permissions load. Nothing by default. */\n loading?: ReactNode;\n}\n\n/**\n * Render `children` only for members who hold `permission`.\n *\n * @example\n * <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.POLICY_CREATE}>\n * <Button>Create Policy</Button>\n * </BotmanorPermissionGate>\n */\nexport const BotmanorPermissionGate: FC<BotmanorPermissionGateProps> = ({\n permission,\n children,\n fallback = null,\n loading = null,\n}) => {\n const decision = resolvePermission(useSafePermissions(), permission);\n if (decision === 'allowed') return <>{children}</>;\n if (decision === 'loading') return <>{loading}</>;\n return <>{fallback}</>;\n};\n"],"mappings":";;;;AAiCA,SAAgB,EACd,GACA,GACoB;AAGpB,QAFK,IACD,EAAQ,YAAkB,YACvB,EAAQ,cAAc,EAAW,GAAG,YAAY,WAFlC;;AAYvB,SAAgB,IAA0C;CACxD,IAAM,IAAU,GAAoB;AACpC,QAAO,GACJ,MAAmC,EAAkB,GAAS,EAAW,KAAK,WAC/E,CAAC,EAAQ,CACV;;AA2BH,IAAa,KAA2D,EACtE,eACA,aACA,cAAW,MACX,aAAU,WACN;CACJ,IAAM,IAAW,EAAkB,GAAoB,EAAE,EAAW;AAGpE,QAFI,MAAa,YAAkB,kBAAA,GAAA,EAAG,aAAY,CAAA,GAC9C,MAAa,YAAkB,kBAAA,GAAA,EAAA,UAAG,GAAW,CAAA,GAC1C,kBAAA,GAAA,EAAA,UAAG,GAAY,CAAA"}
@@ -4,7 +4,8 @@ var e = [
4
4
  "TOOL",
5
5
  "RESPONSE",
6
6
  "BUDGET",
7
- "RATE_LIMIT"
7
+ "RATE_LIMIT",
8
+ "RETENTION"
8
9
  ];
9
10
  function t(t) {
10
11
  return e.includes(t);
@@ -43,44 +44,55 @@ var n = [
43
44
  response: {
44
45
  allowedModels: "",
45
46
  allowedProviders: ""
47
+ },
48
+ retention: {
49
+ executionDataRetentionDays: "90",
50
+ conversationRetentionDays: ""
46
51
  }
47
- };
48
- function a(e) {
52
+ }, a = 3650;
53
+ function o(e) {
49
54
  return e.split(",").map((e) => e.trim()).filter(Boolean);
50
55
  }
51
- function o(e) {
56
+ function s(e) {
52
57
  return e.split("\n").map((e) => e.trim()).filter(Boolean);
53
58
  }
54
- var s = class extends Error {};
55
- function c(e, t) {
59
+ var c = class extends Error {};
60
+ function l(e, t) {
56
61
  let n = e.trim();
57
62
  if (!n) return;
58
63
  let r = Number(n);
59
- if (!Number.isInteger(r) || r < 1) throw new s(`${t} must be a whole number of at least 1, or left empty.`);
64
+ if (!Number.isInteger(r) || r < 1) throw new c(`${t} must be a whole number of at least 1, or left empty.`);
60
65
  return r;
61
66
  }
62
- function l(e) {
67
+ function u(e, t, n) {
68
+ let r = e.trim();
69
+ if (!r) return;
70
+ let i = Number(r);
71
+ if (!Number.isInteger(i) || i < n || i > 3650) throw new c(`${t} must be a whole number of days from ${n} to ${a}, or left empty.`);
72
+ return i;
73
+ }
74
+ function d(e) {
63
75
  for (let t of e) try {
64
76
  new RegExp(t, "i");
65
77
  } catch {
66
- throw new s(`"${t}" is not a valid regular expression.`);
78
+ throw new c(`"${t}" is not a valid regular expression.`);
67
79
  }
68
80
  }
69
- function u(e) {
81
+ function f(e) {
70
82
  return Object.fromEntries(Object.entries(e).filter(([, e]) => e !== void 0 && e !== ""));
71
83
  }
72
- function d(e) {
73
- let t = a(e.blockedKeywords), n = o(e.blockedPatterns);
74
- return l(n), {
84
+ function p(e) {
85
+ let t = o(e.blockedKeywords), n = s(e.blockedPatterns);
86
+ return d(n), {
75
87
  piiDetection: e.piiDetection,
76
88
  ...t.length > 0 ? { blockedKeywords: t } : {},
77
89
  ...n.length > 0 ? { blockedPatterns: n } : {}
78
90
  };
79
91
  }
80
- function f(e) {
81
- let t = a(e.allowedTools), n = a(e.deniedTools), r = u({
82
- maxCallsPerExecution: c(e.maxCallsPerExecution, "Max tool calls per execution"),
83
- timeoutSeconds: c(e.timeoutSeconds, "Tool call timeout")
92
+ function m(e) {
93
+ let t = o(e.allowedTools), n = o(e.deniedTools), r = f({
94
+ maxCallsPerExecution: l(e.maxCallsPerExecution, "Max tool calls per execution"),
95
+ timeoutSeconds: l(e.timeoutSeconds, "Tool call timeout")
84
96
  });
85
97
  return {
86
98
  ...t.length > 0 ? { allowedTools: t } : {},
@@ -90,60 +102,72 @@ function f(e) {
90
102
  ...Object.keys(r).length > 0 ? { limits: r } : {}
91
103
  };
92
104
  }
93
- function p(e) {
94
- return u({
95
- maxTokensPerDay: c(e.maxTokensPerDay, "Max tokens per day"),
96
- maxTokensPerExecution: c(e.maxTokensPerExecution, "Max output tokens per model call")
105
+ function h(e) {
106
+ return f({
107
+ maxTokensPerDay: l(e.maxTokensPerDay, "Max tokens per day"),
108
+ maxTokensPerExecution: l(e.maxTokensPerExecution, "Max output tokens per model call")
97
109
  });
98
110
  }
99
- function m(e) {
100
- let t = u({
101
- requestsPerMinute: c(e.requestsPerMinute, "Requests per minute"),
102
- requestsPerHour: c(e.requestsPerHour, "Requests per hour"),
103
- tokensPerMinute: c(e.tokensPerMinute, "Tokens per minute")
111
+ function g(e) {
112
+ let t = f({
113
+ requestsPerMinute: l(e.requestsPerMinute, "Requests per minute"),
114
+ requestsPerHour: l(e.requestsPerHour, "Requests per hour"),
115
+ tokensPerMinute: l(e.tokensPerMinute, "Tokens per minute")
104
116
  });
105
117
  return Object.keys(t).length > 0 ? { limits: t } : {};
106
118
  }
107
- function h(e) {
108
- let t = a(e.allowedModels), n = a(e.allowedProviders).map((e) => e.toUpperCase());
119
+ function _(e) {
120
+ let t = o(e.allowedModels), n = o(e.allowedProviders).map((e) => e.toUpperCase());
109
121
  return {
110
122
  ...t.length > 0 ? { allowedModels: t } : {},
111
123
  ...n.length > 0 ? { allowedProviders: n } : {}
112
124
  };
113
125
  }
114
- function g(e, t) {
126
+ function v(e) {
127
+ let t = f({
128
+ executionDataRetentionDays: u(e.executionDataRetentionDays, "Execution data retention", 0),
129
+ conversationRetentionDays: u(e.conversationRetentionDays, "Conversation retention", 1)
130
+ });
131
+ if (Object.keys(t).length === 0) throw new c("Set at least one retention period.");
132
+ return t;
133
+ }
134
+ function y(e, t) {
115
135
  try {
116
136
  switch (e) {
117
137
  case "CONTENT": return {
118
138
  ok: !0,
119
- config: d(t.content)
139
+ config: p(t.content)
120
140
  };
121
141
  case "TOOL": return {
122
142
  ok: !0,
123
- config: f(t.tool)
143
+ config: m(t.tool)
124
144
  };
125
145
  case "BUDGET": return {
126
146
  ok: !0,
127
- config: p(t.budget)
147
+ config: h(t.budget)
128
148
  };
129
149
  case "RATE_LIMIT": return {
130
150
  ok: !0,
131
- config: m(t.rateLimit)
151
+ config: g(t.rateLimit)
132
152
  };
133
153
  case "RESPONSE": return {
134
154
  ok: !0,
135
- config: h(t.response)
155
+ config: _(t.response)
156
+ };
157
+ case "RETENTION": return {
158
+ ok: !0,
159
+ config: v(t.retention)
136
160
  };
137
161
  }
138
162
  } catch (e) {
139
- if (e instanceof s) return {
163
+ if (e instanceof c) return {
140
164
  ok: !1,
141
165
  error: e.message
142
166
  };
143
167
  throw e;
144
168
  }
145
169
  }
146
- var _ = {
170
+ var b = {
147
171
  CONTENT: [
148
172
  "piiDetection",
149
173
  "blockedKeywords",
@@ -159,8 +183,9 @@ var _ = {
159
183
  ],
160
184
  RESPONSE: ["allowedModels", "allowedProviders"],
161
185
  BUDGET: ["maxTokensPerDay", "maxTokensPerExecution"],
162
- RATE_LIMIT: ["maxRequestsPerMinute", "limits"]
163
- }, v = {
186
+ RATE_LIMIT: ["maxRequestsPerMinute", "limits"],
187
+ RETENTION: ["executionDataRetentionDays", "conversationRetentionDays"]
188
+ }, x = {
164
189
  TOOL: ["maxCallsPerExecution", "timeoutSeconds"],
165
190
  RATE_LIMIT: [
166
191
  "requestsPerMinute",
@@ -168,8 +193,8 @@ var _ = {
168
193
  "tokensPerMinute"
169
194
  ]
170
195
  };
171
- function y(e, t) {
172
- let n = _[e];
196
+ function S(e, t) {
197
+ let n = b[e];
173
198
  if (!n) return null;
174
199
  if (!t || typeof t != "object" || Array.isArray(t)) return [];
175
200
  let r = [];
@@ -178,12 +203,12 @@ function y(e, t) {
178
203
  r.push(i);
179
204
  continue;
180
205
  }
181
- let t = v[e];
206
+ let t = x[e];
182
207
  if (i === "limits" && t && a && typeof a == "object" && !Array.isArray(a)) for (let e of Object.keys(a)) t.includes(e) || r.push(`limits.${e}`);
183
208
  }
184
209
  return r;
185
210
  }
186
211
  //#endregion
187
- export { r as A2A_TRUST_LEVELS, e as CREATABLE_POLICY_TYPES, i as EMPTY_POLICY_FORM, n as TOOL_RISK_LEVELS, g as buildPolicyConfig, t as isCreatablePolicyType, y as unenforcedConfigKeys };
212
+ export { r as A2A_TRUST_LEVELS, e as CREATABLE_POLICY_TYPES, i as EMPTY_POLICY_FORM, a as MAX_RETENTION_DAYS, n as TOOL_RISK_LEVELS, y as buildPolicyConfig, t as isCreatablePolicyType, S as unenforcedConfigKeys };
188
213
 
189
214
  //# sourceMappingURL=policyConfig.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"policyConfig.js","names":[],"sources":["../../../src/botlit/utils/policyConfig.ts"],"sourcesContent":["/**\n * Policy editor form state <-> the config shape the BotManor runtime enforces.\n *\n * Every key this module writes is read by the backend `PolicyEvaluatorService`\n * (or the tool runtime that applies a resolved TOOL policy). Settings the\n * runtime does not enforce are deliberately absent, so the editor never offers\n * a control that looks binding and is silently ignored (BOFF-9683).\n * @module botlit/utils\n */\n\nimport type { PolicyConfig, PolicyType } from '../types';\n\n/** Policy types the editor can create, i.e. the types with a runtime evaluator. */\nexport const CREATABLE_POLICY_TYPES = [\n 'CONTENT',\n 'TOOL',\n 'RESPONSE',\n 'BUDGET',\n 'RATE_LIMIT',\n] as const;\nexport type CreatablePolicyType = (typeof CREATABLE_POLICY_TYPES)[number];\n\nexport function isCreatablePolicyType(type: PolicyType): type is CreatablePolicyType {\n return (CREATABLE_POLICY_TYPES as readonly string[]).includes(type);\n}\n\nexport const TOOL_RISK_LEVELS = ['LOW', 'MEDIUM', 'HIGH'] as const;\nexport const A2A_TRUST_LEVELS = ['UNVERIFIED', 'VERIFIED_PUBLISHER', 'INTERNAL'] as const;\n\n/**\n * Raw editor state. Numeric fields are strings so a field can be left empty,\n * which means \"not configured\" rather than a default limit.\n */\nexport interface PolicyFormState {\n content: { piiDetection: boolean; blockedKeywords: string; blockedPatterns: string };\n tool: {\n allowedTools: string;\n deniedTools: string;\n maxToolRiskLevel: '' | (typeof TOOL_RISK_LEVELS)[number];\n a2aMinTrustLevel: '' | (typeof A2A_TRUST_LEVELS)[number];\n maxCallsPerExecution: string;\n timeoutSeconds: string;\n };\n budget: { maxTokensPerDay: string; maxTokensPerExecution: string };\n rateLimit: { requestsPerMinute: string; requestsPerHour: string; tokensPerMinute: string };\n response: { allowedModels: string; allowedProviders: string };\n}\n\nexport const EMPTY_POLICY_FORM: PolicyFormState = {\n content: { piiDetection: true, blockedKeywords: '', blockedPatterns: '' },\n tool: {\n allowedTools: '',\n deniedTools: '',\n maxToolRiskLevel: '',\n a2aMinTrustLevel: '',\n maxCallsPerExecution: '',\n timeoutSeconds: '',\n },\n budget: { maxTokensPerDay: '', maxTokensPerExecution: '' },\n rateLimit: { requestsPerMinute: '60', requestsPerHour: '1000', tokensPerMinute: '' },\n response: { allowedModels: '', allowedProviders: '' },\n};\n\nexport type PolicyConfigResult = { ok: true; config: PolicyConfig } | { ok: false; error: string };\n\n/** Split a comma-separated form value into trimmed, non-empty entries. */\nexport function splitList(value: string): string[] {\n return value\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\n/** Split a one-entry-per-line form value into trimmed, non-empty entries. */\nexport function splitLines(value: string): string[] {\n return value\n .split('\\n')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\nclass PolicyFormError extends Error {}\n\n/** Parse an optional positive whole number; empty means \"not configured\". */\nfunction optionalPositiveInt(value: string, label: string): number | undefined {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const parsed = Number(trimmed);\n if (!Number.isInteger(parsed) || parsed < 1) {\n throw new PolicyFormError(`${label} must be a whole number of at least 1, or left empty.`);\n }\n return parsed;\n}\n\nfunction assertValidPatterns(patterns: string[]): void {\n for (const pattern of patterns) {\n try {\n // Compiled only to validate admin input; the runtime compiles the same source.\n new RegExp(pattern, 'i');\n } catch {\n throw new PolicyFormError(`\"${pattern}\" is not a valid regular expression.`);\n }\n }\n}\n\nfunction withDefined<T extends Record<string, unknown>>(entries: T): Partial<T> {\n return Object.fromEntries(\n Object.entries(entries).filter(([, value]) => value !== undefined && value !== '')\n ) as Partial<T>;\n}\n\nfunction buildContentConfig(state: PolicyFormState['content']): PolicyConfig {\n const blockedKeywords = splitList(state.blockedKeywords);\n const blockedPatterns = splitLines(state.blockedPatterns);\n assertValidPatterns(blockedPatterns);\n return {\n piiDetection: state.piiDetection,\n ...(blockedKeywords.length > 0 ? { blockedKeywords } : {}),\n ...(blockedPatterns.length > 0 ? { blockedPatterns } : {}),\n };\n}\n\nfunction buildToolConfig(state: PolicyFormState['tool']): PolicyConfig {\n const allowedTools = splitList(state.allowedTools);\n const deniedTools = splitList(state.deniedTools);\n const limits = withDefined({\n maxCallsPerExecution: optionalPositiveInt(\n state.maxCallsPerExecution,\n 'Max tool calls per execution'\n ),\n timeoutSeconds: optionalPositiveInt(state.timeoutSeconds, 'Tool call timeout'),\n });\n return {\n ...(allowedTools.length > 0 ? { allowedTools } : {}),\n ...(deniedTools.length > 0 ? { deniedTools } : {}),\n ...(state.maxToolRiskLevel ? { maxToolRiskLevel: state.maxToolRiskLevel } : {}),\n ...(state.a2aMinTrustLevel ? { a2aMinTrustLevel: state.a2aMinTrustLevel } : {}),\n ...(Object.keys(limits).length > 0 ? { limits } : {}),\n };\n}\n\nfunction buildBudgetConfig(state: PolicyFormState['budget']): PolicyConfig {\n return withDefined({\n maxTokensPerDay: optionalPositiveInt(state.maxTokensPerDay, 'Max tokens per day'),\n maxTokensPerExecution: optionalPositiveInt(\n state.maxTokensPerExecution,\n 'Max output tokens per model call'\n ),\n });\n}\n\nfunction buildRateLimitConfig(state: PolicyFormState['rateLimit']): PolicyConfig {\n const limits = withDefined({\n requestsPerMinute: optionalPositiveInt(state.requestsPerMinute, 'Requests per minute'),\n requestsPerHour: optionalPositiveInt(state.requestsPerHour, 'Requests per hour'),\n tokensPerMinute: optionalPositiveInt(state.tokensPerMinute, 'Tokens per minute'),\n });\n return Object.keys(limits).length > 0 ? { limits } : {};\n}\n\nfunction buildResponseConfig(state: PolicyFormState['response']): PolicyConfig {\n const allowedModels = splitList(state.allowedModels);\n const allowedProviders = splitList(state.allowedProviders).map((provider) =>\n provider.toUpperCase()\n );\n return {\n ...(allowedModels.length > 0 ? { allowedModels } : {}),\n ...(allowedProviders.length > 0 ? { allowedProviders } : {}),\n };\n}\n\n/**\n * Build the config for a new policy from the editor state, or explain what is\n * wrong with the input. Only enforced keys are ever written.\n */\nexport function buildPolicyConfig(\n type: CreatablePolicyType,\n state: PolicyFormState\n): PolicyConfigResult {\n try {\n switch (type) {\n case 'CONTENT':\n return { ok: true, config: buildContentConfig(state.content) };\n case 'TOOL':\n return { ok: true, config: buildToolConfig(state.tool) };\n case 'BUDGET':\n return { ok: true, config: buildBudgetConfig(state.budget) };\n case 'RATE_LIMIT':\n return { ok: true, config: buildRateLimitConfig(state.rateLimit) };\n case 'RESPONSE':\n return { ok: true, config: buildResponseConfig(state.response) };\n }\n } catch (error) {\n if (error instanceof PolicyFormError) return { ok: false, error: error.message };\n throw error;\n }\n}\n\n/** Config keys the runtime enforces, per policy type (top level). */\nconst ENFORCED_KEYS: Partial<Record<PolicyType, readonly string[]>> = {\n CONTENT: ['piiDetection', 'blockedKeywords', 'blockedPatterns', 'customPatterns'],\n TOOL: ['allowedTools', 'deniedTools', 'maxToolRiskLevel', 'a2aMinTrustLevel', 'limits'],\n RESPONSE: ['allowedModels', 'allowedProviders'],\n BUDGET: ['maxTokensPerDay', 'maxTokensPerExecution'],\n RATE_LIMIT: ['maxRequestsPerMinute', 'limits'],\n};\n\n/** Enforced keys inside `limits`, for the types that nest limits. */\nconst ENFORCED_LIMIT_KEYS: Partial<Record<PolicyType, readonly string[]>> = {\n TOOL: ['maxCallsPerExecution', 'timeoutSeconds'],\n RATE_LIMIT: ['requestsPerMinute', 'requestsPerHour', 'tokensPerMinute'],\n};\n\n/**\n * Config keys on a stored policy that the runtime does not enforce, e.g.\n * settings written by an earlier version of this editor. `null` when the\n * policy type has no runtime evaluator at all.\n */\nexport function unenforcedConfigKeys(type: PolicyType, config: unknown): string[] | null {\n const enforced = ENFORCED_KEYS[type];\n if (!enforced) return null;\n if (!config || typeof config !== 'object' || Array.isArray(config)) return [];\n\n const ignored: string[] = [];\n for (const [key, value] of Object.entries(config as Record<string, unknown>)) {\n if (!enforced.includes(key)) {\n ignored.push(key);\n continue;\n }\n const limitKeys = ENFORCED_LIMIT_KEYS[type];\n if (\n key === 'limits' &&\n limitKeys &&\n value &&\n typeof value === 'object' &&\n !Array.isArray(value)\n ) {\n for (const limitKey of Object.keys(value as Record<string, unknown>)) {\n if (!limitKeys.includes(limitKey)) ignored.push(`limits.${limitKey}`);\n }\n }\n }\n return ignored;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"policyConfig.js","names":[],"sources":["../../../src/botlit/utils/policyConfig.ts"],"sourcesContent":["/**\n * Policy editor form state <-> the config shape the BotManor runtime enforces.\n *\n * Every key this module writes is read by the backend `PolicyEvaluatorService`\n * (or the tool runtime that applies a resolved TOOL policy). Settings the\n * runtime does not enforce are deliberately absent, so the editor never offers\n * a control that looks binding and is silently ignored (BOFF-9683).\n * @module botlit/utils\n */\n\nimport type { PolicyConfig, PolicyType } from '../types';\n\n/** Policy types the editor can create, i.e. the types with a runtime evaluator. */\nexport const CREATABLE_POLICY_TYPES = [\n 'CONTENT',\n 'TOOL',\n 'RESPONSE',\n 'BUDGET',\n 'RATE_LIMIT',\n 'RETENTION',\n] as const;\nexport type CreatablePolicyType = (typeof CREATABLE_POLICY_TYPES)[number];\n\nexport function isCreatablePolicyType(type: PolicyType): type is CreatablePolicyType {\n return (CREATABLE_POLICY_TYPES as readonly string[]).includes(type);\n}\n\nexport const TOOL_RISK_LEVELS = ['LOW', 'MEDIUM', 'HIGH'] as const;\nexport const A2A_TRUST_LEVELS = ['UNVERIFIED', 'VERIFIED_PUBLISHER', 'INTERNAL'] as const;\n\n/**\n * Raw editor state. Numeric fields are strings so a field can be left empty,\n * which means \"not configured\" rather than a default limit.\n */\nexport interface PolicyFormState {\n content: { piiDetection: boolean; blockedKeywords: string; blockedPatterns: string };\n tool: {\n allowedTools: string;\n deniedTools: string;\n maxToolRiskLevel: '' | (typeof TOOL_RISK_LEVELS)[number];\n a2aMinTrustLevel: '' | (typeof A2A_TRUST_LEVELS)[number];\n maxCallsPerExecution: string;\n timeoutSeconds: string;\n };\n budget: { maxTokensPerDay: string; maxTokensPerExecution: string };\n rateLimit: { requestsPerMinute: string; requestsPerHour: string; tokensPerMinute: string };\n response: { allowedModels: string; allowedProviders: string };\n retention: { executionDataRetentionDays: string; conversationRetentionDays: string };\n}\n\nexport const EMPTY_POLICY_FORM: PolicyFormState = {\n content: { piiDetection: true, blockedKeywords: '', blockedPatterns: '' },\n tool: {\n allowedTools: '',\n deniedTools: '',\n maxToolRiskLevel: '',\n a2aMinTrustLevel: '',\n maxCallsPerExecution: '',\n timeoutSeconds: '',\n },\n budget: { maxTokensPerDay: '', maxTokensPerExecution: '' },\n rateLimit: { requestsPerMinute: '60', requestsPerHour: '1000', tokensPerMinute: '' },\n response: { allowedModels: '', allowedProviders: '' },\n retention: { executionDataRetentionDays: '90', conversationRetentionDays: '' },\n};\n\n/** Longest retention period the runtime accepts (10 years). */\nexport const MAX_RETENTION_DAYS = 3650;\n\nexport type PolicyConfigResult = { ok: true; config: PolicyConfig } | { ok: false; error: string };\n\n/** Split a comma-separated form value into trimmed, non-empty entries. */\nexport function splitList(value: string): string[] {\n return value\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\n/** Split a one-entry-per-line form value into trimmed, non-empty entries. */\nexport function splitLines(value: string): string[] {\n return value\n .split('\\n')\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n\nclass PolicyFormError extends Error {}\n\n/** Parse an optional positive whole number; empty means \"not configured\". */\nfunction optionalPositiveInt(value: string, label: string): number | undefined {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const parsed = Number(trimmed);\n if (!Number.isInteger(parsed) || parsed < 1) {\n throw new PolicyFormError(`${label} must be a whole number of at least 1, or left empty.`);\n }\n return parsed;\n}\n\n/** Parse an optional whole number of days in `[min, MAX_RETENTION_DAYS]`; empty means \"not set\". */\nfunction optionalDays(value: string, label: string, min: number): number | undefined {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const parsed = Number(trimmed);\n if (!Number.isInteger(parsed) || parsed < min || parsed > MAX_RETENTION_DAYS) {\n throw new PolicyFormError(\n `${label} must be a whole number of days from ${min} to ${MAX_RETENTION_DAYS}, or left empty.`\n );\n }\n return parsed;\n}\n\nfunction assertValidPatterns(patterns: string[]): void {\n for (const pattern of patterns) {\n try {\n // Compiled only to validate admin input; the runtime compiles the same source.\n new RegExp(pattern, 'i');\n } catch {\n throw new PolicyFormError(`\"${pattern}\" is not a valid regular expression.`);\n }\n }\n}\n\nfunction withDefined<T extends Record<string, unknown>>(entries: T): Partial<T> {\n return Object.fromEntries(\n Object.entries(entries).filter(([, value]) => value !== undefined && value !== '')\n ) as Partial<T>;\n}\n\nfunction buildContentConfig(state: PolicyFormState['content']): PolicyConfig {\n const blockedKeywords = splitList(state.blockedKeywords);\n const blockedPatterns = splitLines(state.blockedPatterns);\n assertValidPatterns(blockedPatterns);\n return {\n piiDetection: state.piiDetection,\n ...(blockedKeywords.length > 0 ? { blockedKeywords } : {}),\n ...(blockedPatterns.length > 0 ? { blockedPatterns } : {}),\n };\n}\n\nfunction buildToolConfig(state: PolicyFormState['tool']): PolicyConfig {\n const allowedTools = splitList(state.allowedTools);\n const deniedTools = splitList(state.deniedTools);\n const limits = withDefined({\n maxCallsPerExecution: optionalPositiveInt(\n state.maxCallsPerExecution,\n 'Max tool calls per execution'\n ),\n timeoutSeconds: optionalPositiveInt(state.timeoutSeconds, 'Tool call timeout'),\n });\n return {\n ...(allowedTools.length > 0 ? { allowedTools } : {}),\n ...(deniedTools.length > 0 ? { deniedTools } : {}),\n ...(state.maxToolRiskLevel ? { maxToolRiskLevel: state.maxToolRiskLevel } : {}),\n ...(state.a2aMinTrustLevel ? { a2aMinTrustLevel: state.a2aMinTrustLevel } : {}),\n ...(Object.keys(limits).length > 0 ? { limits } : {}),\n };\n}\n\nfunction buildBudgetConfig(state: PolicyFormState['budget']): PolicyConfig {\n return withDefined({\n maxTokensPerDay: optionalPositiveInt(state.maxTokensPerDay, 'Max tokens per day'),\n maxTokensPerExecution: optionalPositiveInt(\n state.maxTokensPerExecution,\n 'Max output tokens per model call'\n ),\n });\n}\n\nfunction buildRateLimitConfig(state: PolicyFormState['rateLimit']): PolicyConfig {\n const limits = withDefined({\n requestsPerMinute: optionalPositiveInt(state.requestsPerMinute, 'Requests per minute'),\n requestsPerHour: optionalPositiveInt(state.requestsPerHour, 'Requests per hour'),\n tokensPerMinute: optionalPositiveInt(state.tokensPerMinute, 'Tokens per minute'),\n });\n return Object.keys(limits).length > 0 ? { limits } : {};\n}\n\nfunction buildResponseConfig(state: PolicyFormState['response']): PolicyConfig {\n const allowedModels = splitList(state.allowedModels);\n const allowedProviders = splitList(state.allowedProviders).map((provider) =>\n provider.toUpperCase()\n );\n return {\n ...(allowedModels.length > 0 ? { allowedModels } : {}),\n ...(allowedProviders.length > 0 ? { allowedProviders } : {}),\n };\n}\n\nfunction buildRetentionConfig(state: PolicyFormState['retention']): PolicyConfig {\n const config = withDefined({\n executionDataRetentionDays: optionalDays(\n state.executionDataRetentionDays,\n 'Execution data retention',\n 0\n ),\n conversationRetentionDays: optionalDays(\n state.conversationRetentionDays,\n 'Conversation retention',\n 1\n ),\n });\n if (Object.keys(config).length === 0) {\n throw new PolicyFormError('Set at least one retention period.');\n }\n return config;\n}\n\n/**\n * Build the config for a new policy from the editor state, or explain what is\n * wrong with the input. Only enforced keys are ever written.\n */\nexport function buildPolicyConfig(\n type: CreatablePolicyType,\n state: PolicyFormState\n): PolicyConfigResult {\n try {\n switch (type) {\n case 'CONTENT':\n return { ok: true, config: buildContentConfig(state.content) };\n case 'TOOL':\n return { ok: true, config: buildToolConfig(state.tool) };\n case 'BUDGET':\n return { ok: true, config: buildBudgetConfig(state.budget) };\n case 'RATE_LIMIT':\n return { ok: true, config: buildRateLimitConfig(state.rateLimit) };\n case 'RESPONSE':\n return { ok: true, config: buildResponseConfig(state.response) };\n case 'RETENTION':\n return { ok: true, config: buildRetentionConfig(state.retention) };\n }\n } catch (error) {\n if (error instanceof PolicyFormError) return { ok: false, error: error.message };\n throw error;\n }\n}\n\n/** Config keys the runtime enforces, per policy type (top level). */\nconst ENFORCED_KEYS: Partial<Record<PolicyType, readonly string[]>> = {\n CONTENT: ['piiDetection', 'blockedKeywords', 'blockedPatterns', 'customPatterns'],\n TOOL: ['allowedTools', 'deniedTools', 'maxToolRiskLevel', 'a2aMinTrustLevel', 'limits'],\n RESPONSE: ['allowedModels', 'allowedProviders'],\n BUDGET: ['maxTokensPerDay', 'maxTokensPerExecution'],\n RATE_LIMIT: ['maxRequestsPerMinute', 'limits'],\n RETENTION: ['executionDataRetentionDays', 'conversationRetentionDays'],\n};\n\n/** Enforced keys inside `limits`, for the types that nest limits. */\nconst ENFORCED_LIMIT_KEYS: Partial<Record<PolicyType, readonly string[]>> = {\n TOOL: ['maxCallsPerExecution', 'timeoutSeconds'],\n RATE_LIMIT: ['requestsPerMinute', 'requestsPerHour', 'tokensPerMinute'],\n};\n\n/**\n * Config keys on a stored policy that the runtime does not enforce, e.g.\n * settings written by an earlier version of this editor. `null` when the\n * policy type has no runtime evaluator at all.\n */\nexport function unenforcedConfigKeys(type: PolicyType, config: unknown): string[] | null {\n const enforced = ENFORCED_KEYS[type];\n if (!enforced) return null;\n if (!config || typeof config !== 'object' || Array.isArray(config)) return [];\n\n const ignored: string[] = [];\n for (const [key, value] of Object.entries(config as Record<string, unknown>)) {\n if (!enforced.includes(key)) {\n ignored.push(key);\n continue;\n }\n const limitKeys = ENFORCED_LIMIT_KEYS[type];\n if (\n key === 'limits' &&\n limitKeys &&\n value &&\n typeof value === 'object' &&\n !Array.isArray(value)\n ) {\n for (const limitKey of Object.keys(value as Record<string, unknown>)) {\n if (!limitKeys.includes(limitKey)) ignored.push(`limits.${limitKey}`);\n }\n }\n }\n return ignored;\n}\n"],"mappings":"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"}