solace-agent-mesh 1.7.1__py3-none-any.whl → 1.7.2__py3-none-any.whl

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.

Potentially problematic release.


This version of solace-agent-mesh might be problematic. Click here for more details.

Files changed (130) hide show
  1. solace_agent_mesh/agent/proxies/base/component.py +11 -8
  2. solace_agent_mesh/agent/utils/artifact_helpers.py +45 -0
  3. solace_agent_mesh/assets/docs/404.html +3 -3
  4. solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
  5. solace_agent_mesh/assets/docs/assets/js/60702c0e.f9a9923a.js +1 -0
  6. solace_agent_mesh/assets/docs/assets/js/66d4869e.5d2e116a.js +1 -0
  7. solace_agent_mesh/assets/docs/assets/js/82fbfb93.d98a6526.js +1 -0
  8. solace_agent_mesh/assets/docs/assets/js/924ffdeb.cc09c4ff.js +1 -0
  9. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.9232f4b5.js +1 -0
  10. solace_agent_mesh/assets/docs/assets/js/{e6f9706b.045d0fa1.js → e6f9706b.4488e34c.js} +1 -1
  11. solace_agent_mesh/assets/docs/assets/js/f284c35a.c7ffdd82.js +1 -0
  12. solace_agent_mesh/assets/docs/assets/js/main.60cfcf1b.js +2 -0
  13. solace_agent_mesh/assets/docs/assets/js/runtime~main.ec22999d.js +1 -0
  14. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +3 -3
  15. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +3 -3
  16. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +3 -3
  17. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +3 -3
  18. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +3 -3
  19. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +3 -3
  20. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +3 -3
  21. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +4 -3
  22. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +3 -3
  23. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +3 -3
  24. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +3 -3
  25. solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +4 -18
  26. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +10 -5
  27. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +3 -3
  28. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +3 -3
  29. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +3 -3
  30. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes-deployment/index.html +3 -3
  31. solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +31 -29
  32. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +3 -3
  33. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +3 -3
  34. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +3 -3
  35. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +3 -3
  36. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +3 -3
  37. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +3 -3
  38. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +3 -3
  39. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +3 -3
  40. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +4 -4
  41. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +4 -4
  42. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +4 -4
  43. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +4 -4
  44. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +4 -4
  45. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +4 -4
  46. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +4 -4
  47. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +4 -4
  48. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +4 -4
  49. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/teams-integration/index.html +90 -0
  50. solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +3 -3
  51. solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +3 -3
  52. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +3 -3
  53. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +3 -3
  54. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +3 -3
  55. solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +3 -3
  56. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +3 -3
  57. solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +3 -3
  58. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +3 -3
  59. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +3 -3
  60. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
  61. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +3 -3
  62. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +3 -3
  63. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +3 -3
  64. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +3 -3
  65. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +3 -3
  66. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +3 -3
  67. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +3 -3
  68. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +3 -3
  69. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +3 -3
  70. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
  71. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +3 -3
  72. solace_agent_mesh/assets/docs/lunr-index-1762293572268.json +1 -0
  73. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  74. solace_agent_mesh/assets/docs/search-doc-1762293572268.json +1 -0
  75. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  76. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  77. solace_agent_mesh/cli/__init__.py +1 -1
  78. solace_agent_mesh/cli/commands/eval_cmd.py +1 -1
  79. solace_agent_mesh/cli/commands/init_cmd/env_step.py +1 -1
  80. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +2 -2
  81. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +2 -1
  82. solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +1 -0
  83. solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +3 -3
  84. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-tcIFZLis.js → authCallback-CaZrPAYy.js} +1 -1
  85. solace_agent_mesh/client/webui/frontend/static/assets/{client-CRYdKo2Q.js → client-cVxTmrt6.js} +1 -1
  86. solace_agent_mesh/client/webui/frontend/static/assets/main-BGZP_riA.css +1 -0
  87. solace_agent_mesh/client/webui/frontend/static/assets/main-CBFhYdbQ.js +349 -0
  88. solace_agent_mesh/client/webui/frontend/static/assets/{vendor-CINwxvwV.js → vendor-BzZcWrf0.js} +83 -88
  89. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  90. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  91. solace_agent_mesh/common/sac/sam_component_base.py +44 -0
  92. solace_agent_mesh/config_portal/frontend/static/client/assets/{_index-ByU1X1HD.js → _index-Ch6HyL-d.js} +21 -16
  93. solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-61038fc6.js → manifest-27eb8c5f.js} +1 -1
  94. solace_agent_mesh/config_portal/frontend/static/client/assets/root-B17tZKK7.css +1 -0
  95. solace_agent_mesh/config_portal/frontend/static/client/index.html +2 -2
  96. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +2 -43
  97. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +2 -2
  98. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +8 -103
  99. solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +1 -0
  100. solace_agent_mesh/gateway/http_sse/routers/projects.py +40 -16
  101. solace_agent_mesh/gateway/http_sse/routers/sessions.py +1 -1
  102. solace_agent_mesh/gateway/http_sse/services/project_service.py +47 -1
  103. solace_agent_mesh/gateway/http_sse/services/session_service.py +3 -3
  104. solace_agent_mesh/llm_detail.txt +3 -3
  105. solace_agent_mesh/templates/logging_config_template.yaml +43 -0
  106. solace_agent_mesh/templates/main_orchestrator.yaml +12 -1
  107. solace_agent_mesh/templates/plugin_readme_template.md +3 -25
  108. solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
  109. {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.7.2.dist-info}/METADATA +3 -2
  110. {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.7.2.dist-info}/RECORD +115 -113
  111. solace_agent_mesh/assets/docs/assets/js/15e40e79.36003774.js +0 -1
  112. solace_agent_mesh/assets/docs/assets/js/66d4869e.830d443f.js +0 -1
  113. solace_agent_mesh/assets/docs/assets/js/82fbfb93.139a1a1f.js +0 -1
  114. solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +0 -1
  115. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.eaff365e.js +0 -1
  116. solace_agent_mesh/assets/docs/assets/js/f284c35a.5099c51e.js +0 -1
  117. solace_agent_mesh/assets/docs/assets/js/main.f213fe0c.js +0 -2
  118. solace_agent_mesh/assets/docs/assets/js/runtime~main.d9606d6a.js +0 -1
  119. solace_agent_mesh/assets/docs/lunr-index-1762283454666.json +0 -1
  120. solace_agent_mesh/assets/docs/search-doc-1762283454666.json +0 -1
  121. solace_agent_mesh/client/webui/frontend/static/assets/main-CojeY_1w.css +0 -1
  122. solace_agent_mesh/client/webui/frontend/static/assets/main-ILja9MCG.js +0 -353
  123. solace_agent_mesh/config_portal/frontend/static/client/assets/root-DxRwaWiE.css +0 -1
  124. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_fulltext_search_indexes.py +0 -92
  125. solace_agent_mesh/templates/logging_config_template.ini +0 -45
  126. /solace_agent_mesh/assets/docs/assets/js/{main.f213fe0c.js.LICENSE.txt → main.60cfcf1b.js.LICENSE.txt} +0 -0
  127. /solace_agent_mesh/config_portal/frontend/static/client/assets/{root-BWvk5-gF.js → root-V2BeTIUc.js} +0 -0
  128. {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.7.2.dist-info}/WHEEL +0 -0
  129. {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.7.2.dist-info}/entry_points.txt +0 -0
  130. {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.7.2.dist-info}/licenses/LICENSE +0 -0
@@ -1 +1 @@
1
- window.__remixManifest={"entry":{"module":"/assets/entry.client-mvZjNKiz.js","imports":["/assets/index-DzNKzXrc.js","/assets/components-Rk0n-9cK.js"],"css":[]},"routes":{"root":{"id":"root","path":"","hasAction":false,"hasLoader":false,"hasClientAction":false,"hasClientLoader":false,"hasErrorBoundary":false,"module":"/assets/root-BWvk5-gF.js","imports":["/assets/index-DzNKzXrc.js","/assets/components-Rk0n-9cK.js"],"css":["/assets/root-DxRwaWiE.css"]},"routes/_index":{"id":"routes/_index","parentId":"root","index":true,"hasAction":false,"hasLoader":false,"hasClientAction":false,"hasClientLoader":false,"hasErrorBoundary":false,"module":"/assets/_index-ByU1X1HD.js","imports":["/assets/index-DzNKzXrc.js"],"css":[]}},"url":"/assets/manifest-61038fc6.js","version":"61038fc6"};
1
+ window.__remixManifest={"entry":{"module":"/assets/entry.client-mvZjNKiz.js","imports":["/assets/index-DzNKzXrc.js","/assets/components-Rk0n-9cK.js"],"css":[]},"routes":{"root":{"id":"root","path":"","hasAction":false,"hasLoader":false,"hasClientAction":false,"hasClientLoader":false,"hasErrorBoundary":false,"module":"/assets/root-V2BeTIUc.js","imports":["/assets/index-DzNKzXrc.js","/assets/components-Rk0n-9cK.js"],"css":["/assets/root-B17tZKK7.css"]},"routes/_index":{"id":"routes/_index","parentId":"root","index":true,"hasAction":false,"hasLoader":false,"hasClientAction":false,"hasClientLoader":false,"hasErrorBoundary":false,"module":"/assets/_index-Ch6HyL-d.js","imports":["/assets/index-DzNKzXrc.js"],"css":[]}},"url":"/assets/manifest-27eb8c5f.js","version":"27eb8c5f"};
@@ -0,0 +1 @@
1
+ *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-y-0{top:0;bottom:0}.bottom-0{bottom:0}.left-0{left:0}.left-1{left:.25rem}.right-0{right:0}.right-3{right:.75rem}.top-0{top:0}.top-1{top:.25rem}.top-1\/2{top:50%}.top-5{top:1.25rem}.z-10{z-index:10}.z-50{z-index:50}.col-span-2{grid-column:span 2 / span 2}.-m-6{margin:-1.5rem}.mx-auto{margin-left:auto;margin-right:auto}.-mb-10{margin-bottom:-2.5rem}.-ml-1{margin-left:-.25rem}.-ml-10{margin-left:-2.5rem}.-mr-10{margin-right:-2.5rem}.-mt-10{margin-top:-2.5rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-9{margin-left:2.25rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mr-9{margin-right:2.25rem}.mt-1{margin-top:.25rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-2\.5{margin-top:.625rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.line-clamp-3{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.h-1{height:.25rem}.h-10{height:2.5rem}.h-32{height:8rem}.h-4{height:1rem}.h-40{height:10rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-\[280px\]{height:280px}.h-full{height:100%}.max-h-60{max-height:15rem}.max-h-8{max-height:2rem}.max-h-96{max-height:24rem}.max-h-\[70vh\]{max-height:70vh}.max-h-\[90vh\]{max-height:90vh}.min-h-\[300px\]{min-height:300px}.min-h-\[400px\]{min-height:400px}.min-h-\[40px\]{min-height:40px}.min-h-\[500px\]{min-height:500px}.min-h-screen{min-height:100vh}.w-1\/2{width:50%}.w-1\/3{width:33.333333%}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-20{width:5rem}.w-24{width:6rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-4{width:1rem}.w-40{width:10rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-\[100px\]{min-width:100px}.min-w-full{min-width:100%}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-5xl{max-width:64rem}.max-w-md{max-width:28rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-5{--tw-translate-x: 1.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(229 231 235 / var(--tw-divide-opacity, 1))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-current{border-color:currentColor}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(243 244 246 / var(--tw-border-opacity, 1))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-green-500\/50{border-color:#22c55e80}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity, 1))}.border-red-300{--tw-border-opacity: 1;border-color:rgb(252 165 165 / var(--tw-border-opacity, 1))}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.border-red-500\/50{border-color:#ef444480}.border-slate-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1))}.border-solace-blue{--tw-border-opacity: 1;border-color:rgb(32 52 74 / var(--tw-border-opacity, 1))}.border-solace-green{--tw-border-opacity: 1;border-color:rgb(0 175 131 / var(--tw-border-opacity, 1))}.border-t-transparent{border-top-color:transparent}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\/60{background-color:#0009}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.bg-gray-300{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-gray-600{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.bg-green-100{--tw-bg-opacity: 1;background-color:rgb(220 252 231 / var(--tw-bg-opacity, 1))}.bg-green-200{--tw-bg-opacity: 1;background-color:rgb(187 247 208 / var(--tw-bg-opacity, 1))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.bg-indigo-600{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity, 1))}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-slate-50{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.bg-solace-blue{--tw-bg-opacity: 1;background-color:rgb(32 52 74 / var(--tw-bg-opacity, 1))}.bg-solace-dark-green{--tw-bg-opacity: 1;background-color:rgb(6 143 108 / var(--tw-bg-opacity, 1))}.bg-solace-green{--tw-bg-opacity: 1;background-color:rgb(0 175 131 / var(--tw-bg-opacity, 1))}.bg-solace-light-blue\/10{background-color:#0320341a}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-50{--tw-bg-opacity: 1;background-color:rgb(254 252 232 / var(--tw-bg-opacity, 1))}.bg-opacity-10{--tw-bg-opacity: .1}.bg-opacity-50{--tw-bg-opacity: .5}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-green-50{--tw-gradient-from: #f0fdf4 var(--tw-gradient-from-position);--tw-gradient-to: rgb(240 253 244 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-blue-50{--tw-gradient-to: #eff6ff var(--tw-gradient-to-position)}.object-contain{-o-object-fit:contain;object-fit:contain}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-10{padding:2.5rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pl-1{padding-left:.25rem}.pl-4{padding-left:1rem}.pr-10{padding-right:2.5rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:Inter,ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-800{--tw-text-opacity: 1;color:rgb(30 64 175 / var(--tw-text-opacity, 1))}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-green-800{--tw-text-opacity: 1;color:rgb(22 101 52 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.text-red-800{--tw-text-opacity: 1;color:rgb(153 27 27 / var(--tw-text-opacity, 1))}.text-solace-blue{--tw-text-opacity: 1;color:rgb(32 52 74 / var(--tw-text-opacity, 1))}.text-solace-dark-green{--tw-text-opacity: 1;color:rgb(6 143 108 / var(--tw-text-opacity, 1))}.text-solace-green{--tw-text-opacity: 1;color:rgb(0 175 131 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-800{--tw-text-opacity: 1;color:rgb(133 77 14 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.opacity-100{opacity:1}.opacity-25{opacity:.25}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.opacity-90{opacity:.9}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline{outline-style:solid}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}html,body{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));color-scheme:light}.placeholder\:text-gray-400::-moz-placeholder{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.placeholder\:text-gray-400::placeholder{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.last\:mb-0:last-child{margin-bottom:0}.last\:border-0:last-child{border-width:0px}.last\:pb-0:last-child{padding-bottom:0}.hover\:border-solace-blue\/30:hover{border-color:#20344a4d}.hover\:border-solace-blue\/50:hover{border-color:#20344a80}.hover\:bg-blue-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-300:hover{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\:bg-green-600:hover{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.hover\:bg-green-700:hover{--tw-bg-opacity: 1;background-color:rgb(21 128 61 / var(--tw-bg-opacity, 1))}.hover\:bg-indigo-700:hover{--tw-bg-opacity: 1;background-color:rgb(67 56 202 / var(--tw-bg-opacity, 1))}.hover\:bg-red-50:hover{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.hover\:bg-solace-blue:hover{--tw-bg-opacity: 1;background-color:rgb(32 52 74 / var(--tw-bg-opacity, 1))}.hover\:bg-solace-dark-green:hover{--tw-bg-opacity: 1;background-color:rgb(6 143 108 / var(--tw-bg-opacity, 1))}.hover\:bg-stone-300:hover{--tw-bg-opacity: 1;background-color:rgb(214 211 209 / var(--tw-bg-opacity, 1))}.hover\:text-gray-600:hover{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.hover\:text-gray-900:hover{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.hover\:text-red-800:hover{--tw-text-opacity: 1;color:rgb(153 27 27 / var(--tw-text-opacity, 1))}.hover\:text-solace-blue:hover{--tw-text-opacity: 1;color:rgb(32 52 74 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-md:hover{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus\:border-blue-500:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.focus\:border-solace-blue:focus{--tw-border-opacity: 1;border-color:rgb(32 52 74 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.focus\:ring-gray-400:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(156 163 175 / var(--tw-ring-opacity, 1))}.focus\:ring-gray-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(107 114 128 / var(--tw-ring-opacity, 1))}.focus\:ring-green-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(34 197 94 / var(--tw-ring-opacity, 1))}.focus\:ring-solace-blue:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(32 52 74 / var(--tw-ring-opacity, 1))}.focus\:ring-solace-green:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(0 175 131 / var(--tw-ring-opacity, 1))}.focus\:ring-opacity-50:focus{--tw-ring-opacity: .5}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-blue-500:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.focus-visible\:ring-offset-1:focus-visible{--tw-ring-offset-width: 1px}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-100:disabled{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.disabled\:bg-solace-green\/50:disabled{background-color:#00af8380}.disabled\:text-gray-400:disabled{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.disabled\:text-gray-500:disabled{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:translate-x-1{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:bg-opacity-20{--tw-bg-opacity: .2}.group:hover .group-hover\:text-solace-blue{--tw-text-opacity: 1;color:rgb(32 52 74 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}@media (min-width: 640px){.sm\:col-span-2{grid-column:span 2 / span 2}.sm\:mt-0{margin-top:0}.sm\:grid{display:grid}.sm\:w-1\/2{width:50%}.sm\:w-auto{width:auto}.sm\:min-w-\[140px\]{min-width:140px}.sm\:flex-grow{flex-grow:1}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:gap-4{gap:1rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width: 768px){.md\:hidden{display:none}.md\:flex-grow{flex-grow:1}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:p-6{padding:1.5rem}.md\:p-8{padding:2rem}.md\:text-4xl{font-size:2.25rem;line-height:2.5rem}}@media (min-width: 1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:p-8{padding:2rem}}@media (min-width: 1280px){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (prefers-color-scheme: dark){.dark\:border-gray-600{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity, 1))}.dark\:border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.dark\:border-slate-600{--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity, 1))}.dark\:bg-black\/70{background-color:#000000b3}.dark\:bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.dark\:bg-gray-600{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.dark\:bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.dark\:bg-gray-700\/30{background-color:#3741514d}.dark\:bg-gray-700\/50{background-color:#37415180}.dark\:bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.dark\:bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.dark\:bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.dark\:bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.dark\:bg-green-900\/30{background-color:#14532d4d}.dark\:bg-indigo-500{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity, 1))}.dark\:bg-red-900\/30{background-color:#7f1d1d4d}.dark\:bg-slate-700\/50{background-color:#33415580}.dark\:text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.dark\:text-gray-100{--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}.dark\:text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.dark\:text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.dark\:text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.dark\:text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.dark\:text-green-300{--tw-text-opacity: 1;color:rgb(134 239 172 / var(--tw-text-opacity, 1))}.dark\:text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.dark\:text-red-300{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}.dark\:text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.dark\:placeholder\:text-gray-500::-moz-placeholder{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.dark\:placeholder\:text-gray-500::placeholder{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.dark\:hover\:bg-blue-600:hover{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-gray-500:hover{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-gray-600:hover{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-green-600:hover{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-green-700:hover{--tw-bg-opacity: 1;background-color:rgb(21 128 61 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-indigo-600:hover{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity, 1))}.dark\:hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.dark\:focus-visible\:ring-blue-400:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(96 165 250 / var(--tw-ring-opacity, 1))}.dark\:focus-visible\:ring-offset-gray-800:focus-visible{--tw-ring-offset-color: #1f2937}}
@@ -1,6 +1,6 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/assets/root-DxRwaWiE.css"/><link rel="preconnect" href="https://fonts.googleapis.com"/><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous"/><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&amp;display=swap"/></head><body><p>Loading...</p><link rel="modulepreload" href="/assets/manifest-61038fc6.js"/><link rel="modulepreload" href="/assets/entry.client-mvZjNKiz.js"/><link rel="modulepreload" href="/assets/index-DzNKzXrc.js"/><link rel="modulepreload" href="/assets/components-Rk0n-9cK.js"/><link rel="modulepreload" href="/assets/root-BWvk5-gF.js"/><script>window.__remixContext = {"basename":"/","future":{"v3_fetcherPersist":false,"v3_relativeSplatPath":false,"v3_throwAbortReason":false,"v3_routeConfig":false,"v3_singleFetch":false,"v3_lazyRouteDiscovery":false,"unstable_optimizeDeps":false},"isSpaMode":true,"state":{"loaderData":{"root":null,"routes/_index":null},"actionData":null,"errors":null}};</script><script type="module" async="">import "/assets/manifest-61038fc6.js";
3
- import * as route0 from "/assets/root-BWvk5-gF.js";
2
+ <html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/assets/root-B17tZKK7.css"/><link rel="preconnect" href="https://fonts.googleapis.com"/><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous"/><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&amp;display=swap"/></head><body><p>Loading...</p><link rel="modulepreload" href="/assets/manifest-27eb8c5f.js"/><link rel="modulepreload" href="/assets/entry.client-mvZjNKiz.js"/><link rel="modulepreload" href="/assets/index-DzNKzXrc.js"/><link rel="modulepreload" href="/assets/components-Rk0n-9cK.js"/><link rel="modulepreload" href="/assets/root-V2BeTIUc.js"/><script>window.__remixContext = {"basename":"/","future":{"v3_fetcherPersist":false,"v3_relativeSplatPath":false,"v3_throwAbortReason":false,"v3_routeConfig":false,"v3_singleFetch":false,"v3_lazyRouteDiscovery":false,"unstable_optimizeDeps":false},"isSpaMode":true,"state":{"loaderData":{"root":null,"routes/_index":null},"actionData":null,"errors":null}};</script><script type="module" async="">import "/assets/manifest-27eb8c5f.js";
3
+ import * as route0 from "/assets/root-V2BeTIUc.js";
4
4
 
5
5
  window.__remixRouteModules = {"root":route0};
6
6
 
@@ -65,56 +65,15 @@ def upgrade() -> None:
65
65
  op.create_index('ix_projects_user_deleted', 'projects', ['user_id', 'deleted_at'])
66
66
  except Exception:
67
67
  pass
68
-
69
- # Enable pg_trgm extension for fuzzy text search (PostgreSQL only)
70
- try:
71
- op.execute('CREATE EXTENSION IF NOT EXISTS pg_trgm')
72
- except Exception:
73
- # Not PostgreSQL or extension already exists
74
- pass
75
-
76
- # Create GIN indexes for text search on sessions
77
- if 'sessions' in inspector.get_table_names():
78
- try:
79
- # Index on session name for fuzzy search
80
- op.execute(
81
- 'CREATE INDEX IF NOT EXISTS ix_sessions_name_trgm ON sessions '
82
- 'USING gin (name gin_trgm_ops)'
83
- )
84
- except Exception:
85
- pass
86
-
87
- # Create GIN indexes for text search on chat_tasks
88
- if 'chat_tasks' in inspector.get_table_names():
89
- try:
90
- # Index on user_message for content search
91
- op.execute(
92
- 'CREATE INDEX IF NOT EXISTS ix_chat_tasks_user_message_trgm ON chat_tasks '
93
- 'USING gin (user_message gin_trgm_ops)'
94
- )
95
- except Exception:
96
- pass
97
68
 
98
69
 
99
70
  def downgrade() -> None:
100
71
  """Remove soft delete columns and search indexes."""
101
72
  bind = op.get_bind()
102
73
  inspector = inspect(bind)
103
-
104
- # Drop search indexes from chat_tasks
105
- if 'chat_tasks' in inspector.get_table_names():
106
- try:
107
- op.execute('DROP INDEX IF EXISTS ix_chat_tasks_user_message_trgm')
108
- except Exception:
109
- pass
110
-
111
- # Drop search indexes from sessions
74
+
75
+ # Drop soft delete indexes from sessions
112
76
  if 'sessions' in inspector.get_table_names():
113
- try:
114
- op.execute('DROP INDEX IF EXISTS ix_sessions_name_trgm')
115
- except Exception:
116
- pass
117
-
118
77
  try:
119
78
  op.drop_index('ix_sessions_user_deleted', table_name='sessions')
120
79
  except Exception:
@@ -1,7 +1,7 @@
1
1
  """add default agent to projects
2
2
 
3
3
  Revision ID: default_agent_001
4
- Revises: fts_indexes_001
4
+ Revises: soft_del_search_001
5
5
  Create Date: 2025-01-24 01:13:00.000000
6
6
 
7
7
  """
@@ -11,7 +11,7 @@ import sqlalchemy as sa
11
11
 
12
12
  # revision identifiers, used by Alembic.
13
13
  revision = 'default_agent_001'
14
- down_revision = 'fts_indexes_001'
14
+ down_revision = 'soft_del_search_001'
15
15
  branch_labels = None
16
16
  depends_on = None
17
17
 
@@ -12,7 +12,6 @@ from ..shared import now_epoch_ms
12
12
  from .entities import Session
13
13
  from .interfaces import ISessionRepository
14
14
  from .models import CreateSessionModel, SessionModel, UpdateSessionModel
15
- from .models.chat_task_model import ChatTaskModel
16
15
 
17
16
 
18
17
  class SessionRepository(PaginatedRepository[SessionModel, Session], ISessionRepository):
@@ -217,10 +216,7 @@ class SessionRepository(PaginatedRepository[SessionModel, Session], ISessionRepo
217
216
  project_id: str | None = None
218
217
  ) -> list[Session]:
219
218
  """
220
- Search sessions by name or content.
221
-
222
- Uses PostgreSQL full-text search when available (for queries >= 3 chars),
223
- falls back to ILIKE for SQLite or short queries.
219
+ Search sessions by name/title only using ILIKE.
224
220
  """
225
221
  # Base query - only non-deleted sessions for the user
226
222
  base_query = db_session.query(SessionModel).filter(
@@ -232,54 +228,9 @@ class SessionRepository(PaginatedRepository[SessionModel, Session], ISessionRepo
232
228
  if project_id is not None:
233
229
  base_query = base_query.filter(SessionModel.project_id == project_id)
234
230
 
235
- # Detect database dialect
236
- dialect_name = db_session.bind.dialect.name
237
- use_fts = dialect_name == 'postgresql' and len(query.strip()) >= 3
238
-
239
- if use_fts:
240
- # PostgreSQL full-text search for better performance
241
- matching_chat_tasks = (
242
- db_session.query(ChatTaskModel.session_id)
243
- .filter(
244
- or_(
245
- func.to_tsvector('english', func.coalesce(ChatTaskModel.user_message, ''))
246
- .op('@@')(func.plainto_tsquery('english', query)),
247
- func.to_tsvector('english', ChatTaskModel.message_bubbles)
248
- .op('@@')(func.plainto_tsquery('english', query))
249
- )
250
- )
251
- .distinct()
252
- .subquery()
253
- )
254
-
255
- search_query = base_query.filter(
256
- or_(
257
- func.to_tsvector('english', func.coalesce(SessionModel.name, ''))
258
- .op('@@')(func.plainto_tsquery('english', query)),
259
- SessionModel.id.in_(matching_chat_tasks)
260
- )
261
- )
262
- else:
263
- # ILIKE search for SQLite or short queries
264
- search_pattern = f"%{query}%"
265
- matching_chat_tasks = (
266
- db_session.query(ChatTaskModel.session_id)
267
- .filter(
268
- or_(
269
- ChatTaskModel.user_message.ilike(search_pattern),
270
- ChatTaskModel.message_bubbles.ilike(search_pattern)
271
- )
272
- )
273
- .distinct()
274
- .subquery()
275
- )
276
-
277
- search_query = base_query.filter(
278
- or_(
279
- SessionModel.name.ilike(search_pattern),
280
- SessionModel.id.in_(matching_chat_tasks)
281
- )
282
- )
231
+ # ILIKE search on session name
232
+ search_pattern = f"%{query}%"
233
+ search_query = base_query.filter(SessionModel.name.ilike(search_pattern))
283
234
 
284
235
  # Eager load project relationship
285
236
  search_query = search_query.options(joinedload(SessionModel.project))
@@ -299,9 +250,7 @@ class SessionRepository(PaginatedRepository[SessionModel, Session], ISessionRepo
299
250
  project_id: str | None = None
300
251
  ) -> int:
301
252
  """
302
- Count search results for pagination.
303
-
304
- Uses same database-agnostic logic as search() method for consistency.
253
+ Count search results for pagination (title-only search).
305
254
  """
306
255
  # Base query - only non-deleted sessions for the user
307
256
  base_query = db_session.query(SessionModel).filter(
@@ -312,52 +261,8 @@ class SessionRepository(PaginatedRepository[SessionModel, Session], ISessionRepo
312
261
  if project_id is not None:
313
262
  base_query = base_query.filter(SessionModel.project_id == project_id)
314
263
 
315
- dialect_name = db_session.bind.dialect.name
316
- use_fts = dialect_name == 'postgresql' and len(query.strip()) >= 3
317
-
318
- if use_fts:
319
- # PostgreSQL full-text search
320
- matching_chat_tasks = (
321
- db_session.query(ChatTaskModel.session_id)
322
- .filter(
323
- or_(
324
- func.to_tsvector('english', func.coalesce(ChatTaskModel.user_message, ''))
325
- .op('@@')(func.plainto_tsquery('english', query)),
326
- func.to_tsvector('english', ChatTaskModel.message_bubbles)
327
- .op('@@')(func.plainto_tsquery('english', query))
328
- )
329
- )
330
- .distinct()
331
- .subquery()
332
- )
333
-
334
- search_query = base_query.filter(
335
- or_(
336
- func.to_tsvector('english', func.coalesce(SessionModel.name, ''))
337
- .op('@@')(func.plainto_tsquery('english', query)),
338
- SessionModel.id.in_(matching_chat_tasks)
339
- )
340
- )
341
- else:
342
- # ILIKE search for SQLite or short queries
343
- search_pattern = f"%{query}%"
344
- matching_chat_tasks = (
345
- db_session.query(ChatTaskModel.session_id)
346
- .filter(
347
- or_(
348
- ChatTaskModel.user_message.ilike(search_pattern),
349
- ChatTaskModel.message_bubbles.ilike(search_pattern)
350
- )
351
- )
352
- .distinct()
353
- .subquery()
354
- )
355
-
356
- search_query = base_query.filter(
357
- or_(
358
- SessionModel.name.ilike(search_pattern),
359
- SessionModel.id.in_(matching_chat_tasks)
360
- )
361
- )
264
+ # ILIKE search on session name
265
+ search_pattern = f"%{query}%"
266
+ search_query = base_query.filter(SessionModel.name.ilike(search_pattern))
362
267
 
363
268
  return search_query.count()
@@ -17,6 +17,7 @@ class ProjectResponse(BaseTimestampResponse):
17
17
  description: Optional[str] = None
18
18
  system_prompt: Optional[str] = Field(default=None, alias="systemPrompt")
19
19
  default_agent_id: Optional[str] = Field(default=None, alias="defaultAgentId")
20
+ artifact_count: Optional[int] = Field(default=None, alias="artifactCount")
20
21
  created_at: int = Field(alias="createdAt")
21
22
  updated_at: Optional[int] = Field(default=None, alias="updatedAt")
22
23
 
@@ -165,6 +165,7 @@ async def create_project(
165
165
 
166
166
  @router.get("/projects", response_model=ProjectListResponse)
167
167
  async def get_user_projects(
168
+ include_artifact_count: bool = False,
168
169
  user: dict = Depends(get_current_user),
169
170
  project_service: ProjectService = Depends(get_project_service),
170
171
  db: Session = Depends(get_db),
@@ -172,28 +173,51 @@ async def get_user_projects(
172
173
  ):
173
174
  """
174
175
  Get all projects owned by the authenticated user.
176
+
177
+ Args:
178
+ include_artifact_count: If True, includes artifact count for each project
175
179
  """
176
180
  user_id = user.get("id")
177
- log.info(f"Fetching projects for user_id: {user_id}")
181
+ log.info(f"Fetching projects for user_id: {user_id}, include_artifact_count: {include_artifact_count}")
178
182
 
179
183
  try:
180
184
  request_dto = GetProjectsRequest(user_id=user_id)
181
185
 
182
- projects = project_service.get_user_projects(db, request_dto.user_id)
183
-
184
- project_responses = [
185
- ProjectResponse(
186
- id=p.id,
187
- name=p.name,
188
- user_id=p.user_id,
189
- description=p.description,
190
- system_prompt=p.system_prompt,
191
- default_agent_id=p.default_agent_id,
192
- created_at=p.created_at,
193
- updated_at=p.updated_at,
194
- )
195
- for p in projects
196
- ]
186
+ if include_artifact_count:
187
+ # Fetch projects with artifact counts
188
+ projects_with_counts = await project_service.get_user_projects_with_counts(db, request_dto.user_id)
189
+
190
+ project_responses = [
191
+ ProjectResponse(
192
+ id=p.id,
193
+ name=p.name,
194
+ user_id=p.user_id,
195
+ description=p.description,
196
+ system_prompt=p.system_prompt,
197
+ default_agent_id=p.default_agent_id,
198
+ artifact_count=count,
199
+ created_at=p.created_at,
200
+ updated_at=p.updated_at,
201
+ )
202
+ for p, count in projects_with_counts
203
+ ]
204
+ else:
205
+ # Fetch projects without counts (faster)
206
+ projects = project_service.get_user_projects(db, request_dto.user_id)
207
+
208
+ project_responses = [
209
+ ProjectResponse(
210
+ id=p.id,
211
+ name=p.name,
212
+ user_id=p.user_id,
213
+ description=p.description,
214
+ system_prompt=p.system_prompt,
215
+ default_agent_id=p.default_agent_id,
216
+ created_at=p.created_at,
217
+ updated_at=p.updated_at,
218
+ )
219
+ for p in projects
220
+ ]
197
221
 
198
222
  return ProjectListResponse(
199
223
  projects=project_responses,
@@ -79,7 +79,7 @@ async def search_sessions(
79
79
  session_service: SessionService = Depends(get_session_business_service),
80
80
  ):
81
81
  """
82
- Search sessions by name or content.
82
+ Search sessions by name/title only.
83
83
  """
84
84
  user_id = user.get("id")
85
85
  log.info(
@@ -7,7 +7,7 @@ import logging
7
7
  from fastapi import UploadFile
8
8
  from datetime import datetime, timezone
9
9
 
10
- from ....agent.utils.artifact_helpers import get_artifact_info_list, save_artifact_with_metadata
10
+ from ....agent.utils.artifact_helpers import get_artifact_info_list, save_artifact_with_metadata, get_artifact_counts_batch
11
11
 
12
12
  try:
13
13
  from google.adk.artifacts import BaseArtifactService
@@ -162,6 +162,52 @@ class ProjectService:
162
162
  db_projects = project_repository.get_user_projects(user_id)
163
163
  return db_projects
164
164
 
165
+ async def get_user_projects_with_counts(self, db, user_id: str) -> List[tuple[Project, int]]:
166
+ """
167
+ Get all projects owned by a specific user with artifact counts.
168
+ Uses batch counting for efficiency.
169
+
170
+ Args:
171
+ db: Database session
172
+ user_id: The user ID
173
+
174
+ Returns:
175
+ List[tuple[Project, int]]: List of tuples (project, artifact_count)
176
+ """
177
+ self.logger.debug(f"Retrieving projects with artifact counts for user {user_id}")
178
+ projects = self.get_user_projects(db, user_id)
179
+
180
+ if not self.artifact_service or not projects:
181
+ # If no artifact service or no projects, return projects with 0 counts
182
+ return [(project, 0) for project in projects]
183
+
184
+ # Build list of session IDs for batch counting
185
+ session_ids = [f"project-{project.id}" for project in projects]
186
+
187
+ try:
188
+ # Get all counts in a single batch operation
189
+ counts_by_session = await get_artifact_counts_batch(
190
+ artifact_service=self.artifact_service,
191
+ app_name=self.app_name,
192
+ user_id=user_id,
193
+ session_ids=session_ids,
194
+ )
195
+
196
+ # Map counts back to projects
197
+ projects_with_counts = []
198
+ for project in projects:
199
+ storage_session_id = f"project-{project.id}"
200
+ artifact_count = counts_by_session.get(storage_session_id, 0)
201
+ projects_with_counts.append((project, artifact_count))
202
+
203
+ self.logger.debug(f"Retrieved artifact counts for {len(projects)} projects in batch")
204
+ return projects_with_counts
205
+
206
+ except Exception as e:
207
+ self.logger.error(f"Failed to get artifact counts in batch: {e}")
208
+ # Fallback to 0 counts on error
209
+ return [(project, 0) for project in projects]
210
+
165
211
  async def get_project_artifacts(self, db, project_id: str, user_id: str) -> List[ArtifactInfo]:
166
212
  """
167
213
  Get a list of artifacts for a given project.
@@ -291,15 +291,15 @@ class SessionService:
291
291
  project_id: str | None = None
292
292
  ) -> PaginatedResponse[Session]:
293
293
  """
294
- Search sessions by name or content.
295
-
294
+ Search sessions by name/title only.
295
+
296
296
  Args:
297
297
  db: Database session
298
298
  user_id: User ID to filter sessions by
299
299
  query: Search query string
300
300
  pagination: Pagination parameters
301
301
  project_id: Optional project ID to filter sessions by
302
-
302
+
303
303
  Returns:
304
304
  PaginatedResponse[Session]: Paginated search results
305
305
  """
@@ -789,7 +789,7 @@ The `client` directory provides a Python-based client library for Agent-to-Agent
789
789
  **Import:** `from src.solace_agent_mesh.common.client import A2ACardResolver`
790
790
 
791
791
  **Classes:**
792
- - `A2ACardResolver(base_url: str, agent_card_path: str = "/.well-known/agent.json")` - A client to discover and fetch an agent's capability card.
792
+ - `A2ACardResolver(base_url: str, agent_card_path: str = "/.well-known/agent-card.json")` - A client to discover and fetch an agent's capability card.
793
793
  - `get_agent_card() -> AgentCard` - Makes an HTTP GET request to the constructed agent card URL, parses the JSON response, and returns it as an `AgentCard` object. Raises `A2AClientHTTPError` on network/status errors and `A2AClientJSONError` on parsing errors.
794
794
 
795
795
  **Usage Examples:**
@@ -803,7 +803,7 @@ resolver = A2ACardResolver(base_url="https://some-agent.ai")
803
803
 
804
804
  try:
805
805
  # Fetch the agent's capability card from the default path
806
- # (https://some-agent.ai/.well-known/agent.json)
806
+ # (https://some-agent.ai/.well-known/agent-card.json)
807
807
  agent_card: AgentCard = resolver.get_agent_card()
808
808
  print(f"Successfully fetched card for agent: {agent_card.name}")
809
809
  print(f"Agent API URL: {agent_card.url}")
@@ -1265,7 +1265,7 @@ The `server` directory provides a complete, stand-alone Agent-to-Agent (A2A) com
1265
1265
  - `port: int` - The port the server will listen on.
1266
1266
  - `endpoint: str` - The main API endpoint path for receiving JSON-RPC requests.
1267
1267
  - `task_manager: TaskManager` - The handler responsible for all task-related business logic.
1268
- - `agent_card: AgentCard` - The metadata for the agent, served at `/.well-known/agent.json`.
1268
+ - `agent_card: AgentCard` - The metadata for the agent, served at `/.well-known/agent-card.json`.
1269
1269
 
1270
1270
  **Usage Examples:**
1271
1271
  ```python
@@ -0,0 +1,43 @@
1
+ version: 1
2
+ disable_existing_loggers: false
3
+
4
+ formatters:
5
+ simpleFormatter:
6
+ format: "%(asctime)s | %(levelname)-5s | %(threadName)s | %(name)s | %(message)s"
7
+
8
+ jsonFormatter:
9
+ "()": pythonjsonlogger.jsonlogger.JsonFormatter
10
+ format: "%(asctime)s %(levelname)s %(threadName)s %(name)s %(message)s"
11
+
12
+ handlers:
13
+ streamHandler:
14
+ class: logging.StreamHandler
15
+ formatter: simpleFormatter
16
+ stream: ext://sys.stdout
17
+
18
+ rotatingFileHandler:
19
+ class: logging.handlers.RotatingFileHandler
20
+ formatter: simpleFormatter
21
+ filename: sam.log
22
+ mode: a
23
+ maxBytes: 52428800
24
+ backupCount: 10
25
+
26
+ loggers:
27
+ solace_ai_connector:
28
+ level: ${LOGGING_SAC_LEVEL, INFO}
29
+ handlers: []
30
+
31
+ solace_agent_mesh:
32
+ level: ${LOGGING_SAM_LEVEL, INFO}
33
+ handlers: []
34
+
35
+ sam_trace:
36
+ level: ${LOGGING_SAM_TRACE_LEVEL, INFO}
37
+ handlers: []
38
+
39
+ root:
40
+ level: ${LOGGING_ROOT_LEVEL, WARNING}
41
+ handlers:
42
+ - streamHandler
43
+ - rotatingFileHandler
@@ -43,7 +43,18 @@ apps:
43
43
  description: "__AGENT_CARD_DESCRIPTION__"
44
44
  defaultInputModes: __DEFAULT_INPUT_MODES__
45
45
  defaultOutputModes: __DEFAULT_OUTPUT_MODES__
46
- skills: []
46
+ skills:
47
+ - id: strategic_planning
48
+ name: Strategic Planning
49
+ description: Analyzes complex requests and creates structured execution plans, breaking down multi-step goals into logical sequences with clear checkpoints and progress tracking.
50
+
51
+ - id: agent_coordination
52
+ name: Agent Coordination
53
+ description: Identifies the most suitable specialized agents for specific tasks and coordinates multi-agent workflows, managing data handoffs and execution sequences to achieve complex objectives efficiently.
54
+
55
+ - id: artifact_management
56
+ name: Artifact Management
57
+ description: Creates, transforms, and manages various artifact types including documents, reports, and data files, with capabilities for content generation, formatting, embedding dynamic data, and applying sophisticated data transformations.
47
58
 
48
59
  agent_card_publishing:
49
60
  interval_seconds: __AGENT_CARD_PUBLISHING_INTERVAL__
@@ -1,34 +1,12 @@
1
- # __PLUGIN_KEBAB_CASE_NAME__ SAM Plugin
1
+ # __PLUGIN_PASCAL_CASE_NAME__ SAM Plugin
2
2
 
3
3
  __PLUGIN_DESCRIPTION__
4
4
 
5
5
  This is a plugin for Solace Agent Mesh (SAM).
6
6
 
7
- ## Configuration (`config.yaml`)
8
-
9
- The `config.yaml` in this plugin serves as a template. When you use `sam plugin add <component_name> --plugin __PLUGIN_KEBAB_CASE_NAME__`, the following placeholders in the YAML structure will be replaced with variations of `<component_name>`:
10
- - `__COMPONENT_UPPER_SNAKE_CASE_NAME__`
11
- - `__COMPONENT_KEBAB_CASE_NAME__`
12
- - `__COMPONENT_PASCAL_CASE_NAME__`
13
-
14
- Customize the `config.yaml` in this plugin directory to define the base configuration for components created from it.
15
-
16
- ## Source Code (`src/`)
17
- The `src/` directory contains the Python source code for your plugin.
18
-
19
- ## Installation (as a developer of this plugin)
20
-
21
- To build and install this plugin locally for testing:
22
- ```bash
23
- sam plugin build
24
- pip install dist/*.whl
25
- ```
26
- (Or `pip install .` if preferred, `sam plugin build` is for creating the distributable wheel)
27
-
28
- ## Usage (as a user of this plugin)
7
+ ## Installation
29
8
 
30
9
  Once the plugin is installed (e.g., from PyPI or a local wheel file):
31
10
  ```bash
32
11
  sam plugin add <your-new-component-name> --plugin __PLUGIN_KEBAB_CASE_NAME__
33
- ```
34
- This will create a new component configuration at `configs/plugins/<your-new-component-name-kebab-case>.yaml`.
12
+ ```