#
tokens: 46918/50000 3/1784 files (page 92/145)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 92 of 145. Use http://codebase.md/microsoft/semanticworkbench?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .devcontainer
│   ├── .vscode
│   │   └── settings.json
│   ├── devcontainer.json
│   ├── OPTIMIZING_FOR_CODESPACES.md
│   ├── POST_SETUP_README.md
│   └── README.md
├── .dockerignore
├── .gitattributes
├── .github
│   ├── policheck.yml
│   └── workflows
│       ├── assistants-codespace-assistant.yml
│       ├── assistants-document-assistant.yml
│       ├── assistants-explorer-assistant.yml
│       ├── assistants-guided-conversation-assistant.yml
│       ├── assistants-knowledge-transfer-assistant.yml
│       ├── assistants-navigator-assistant.yml
│       ├── assistants-project-assistant.yml
│       ├── assistants-prospector-assistant.yml
│       ├── assistants-skill-assistant.yml
│       ├── libraries.yml
│       ├── mcp-server-giphy.yml
│       ├── mcp-server-memory-filesystem-edit.yml
│       ├── mcp-server-memory-user-bio.yml
│       ├── mcp-server-memory-whiteboard.yml
│       ├── mcp-server-open-deep-research-clone.yml
│       ├── mcp-server-web-research.yml
│       ├── workbench-app.yml
│       └── workbench-service.yml
├── .gitignore
├── .multi-root-tools
│   ├── Makefile
│   └── README.md
├── .vscode
│   ├── extensions.json
│   ├── launch.json
│   └── settings.json
├── ai_context
│   └── generated
│       ├── ASPIRE_ORCHESTRATOR.md
│       ├── ASSISTANT_CODESPACE.md
│       ├── ASSISTANT_DOCUMENT.md
│       ├── ASSISTANT_NAVIGATOR.md
│       ├── ASSISTANT_PROJECT.md
│       ├── ASSISTANT_PROSPECTOR.md
│       ├── ASSISTANTS_OTHER.md
│       ├── ASSISTANTS_OVERVIEW.md
│       ├── CONFIGURATION.md
│       ├── DOTNET_LIBRARIES.md
│       ├── EXAMPLES.md
│       ├── MCP_SERVERS.md
│       ├── PYTHON_LIBRARIES_AI_CLIENTS.md
│       ├── PYTHON_LIBRARIES_CORE.md
│       ├── PYTHON_LIBRARIES_EXTENSIONS.md
│       ├── PYTHON_LIBRARIES_SKILLS.md
│       ├── PYTHON_LIBRARIES_SPECIALIZED.md
│       ├── TOOLS.md
│       ├── WORKBENCH_FRONTEND.md
│       └── WORKBENCH_SERVICE.md
├── aspire-orchestrator
│   ├── .editorconfig
│   ├── Aspire.AppHost
│   │   ├── .gitignore
│   │   ├── appsettings.json
│   │   ├── Aspire.AppHost.csproj
│   │   ├── Program.cs
│   │   └── Properties
│   │       └── launchSettings.json
│   ├── Aspire.Extensions
│   │   ├── Aspire.Extensions.csproj
│   │   ├── Dashboard.cs
│   │   ├── DockerFileExtensions.cs
│   │   ├── PathNormalizer.cs
│   │   ├── UvAppHostingExtensions.cs
│   │   ├── UvAppResource.cs
│   │   ├── VirtualEnvironment.cs
│   │   └── WorkbenchServiceHostingExtensions.cs
│   ├── Aspire.ServiceDefaults
│   │   ├── Aspire.ServiceDefaults.csproj
│   │   └── Extensions.cs
│   ├── README.md
│   ├── run.sh
│   ├── SemanticWorkbench.Aspire.sln
│   └── SemanticWorkbench.Aspire.sln.DotSettings
├── assistants
│   ├── codespace-assistant
│   │   ├── .claude
│   │   │   └── settings.local.json
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── extensions.json
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── assets
│   │   │   │   ├── icon_context_transfer.svg
│   │   │   │   └── icon.svg
│   │   │   ├── chat.py
│   │   │   ├── config.py
│   │   │   ├── helpers.py
│   │   │   ├── response
│   │   │   │   ├── __init__.py
│   │   │   │   ├── completion_handler.py
│   │   │   │   ├── models.py
│   │   │   │   ├── request_builder.py
│   │   │   │   ├── response.py
│   │   │   │   ├── step_handler.py
│   │   │   │   └── utils
│   │   │   │       ├── __init__.py
│   │   │   │       ├── abbreviations.py
│   │   │   │       ├── formatting_utils.py
│   │   │   │       ├── message_utils.py
│   │   │   │       └── openai_utils.py
│   │   │   ├── text_includes
│   │   │   │   ├── card_content_context_transfer.md
│   │   │   │   ├── card_content.md
│   │   │   │   ├── codespace_assistant_info.md
│   │   │   │   ├── context_transfer_assistant_info.md
│   │   │   │   ├── guardrails_prompt.txt
│   │   │   │   ├── guidance_prompt_context_transfer.txt
│   │   │   │   ├── guidance_prompt.txt
│   │   │   │   ├── instruction_prompt_context_transfer.txt
│   │   │   │   └── instruction_prompt.txt
│   │   │   └── whiteboard
│   │   │       ├── __init__.py
│   │   │       ├── _inspector.py
│   │   │       └── _whiteboard.py
│   │   ├── assistant.code-workspace
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── document-assistant
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── assets
│   │   │   │   └── icon.svg
│   │   │   ├── chat.py
│   │   │   ├── config.py
│   │   │   ├── context_management
│   │   │   │   ├── __init__.py
│   │   │   │   └── inspector.py
│   │   │   ├── filesystem
│   │   │   │   ├── __init__.py
│   │   │   │   ├── _convert.py
│   │   │   │   ├── _file_sources.py
│   │   │   │   ├── _filesystem.py
│   │   │   │   ├── _inspector.py
│   │   │   │   ├── _model.py
│   │   │   │   ├── _prompts.py
│   │   │   │   └── _tasks.py
│   │   │   ├── guidance
│   │   │   │   ├── __init__.py
│   │   │   │   ├── dynamic_ui_inspector.py
│   │   │   │   ├── guidance_config.py
│   │   │   │   ├── guidance_prompts.py
│   │   │   │   └── README.md
│   │   │   ├── response
│   │   │   │   ├── __init__.py
│   │   │   │   ├── completion_handler.py
│   │   │   │   ├── models.py
│   │   │   │   ├── prompts.py
│   │   │   │   ├── responder.py
│   │   │   │   └── utils
│   │   │   │       ├── __init__.py
│   │   │   │       ├── formatting_utils.py
│   │   │   │       ├── message_utils.py
│   │   │   │       ├── openai_utils.py
│   │   │   │       ├── tokens_tiktoken.py
│   │   │   │       └── workbench_messages.py
│   │   │   ├── text_includes
│   │   │   │   └── document_assistant_info.md
│   │   │   ├── types.py
│   │   │   └── whiteboard
│   │   │       ├── __init__.py
│   │   │       ├── _inspector.py
│   │   │       └── _whiteboard.py
│   │   ├── assistant.code-workspace
│   │   ├── CLAUDE.md
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   ├── __init__.py
│   │   │   ├── test_convert.py
│   │   │   └── test_data
│   │   │       ├── blank_image.png
│   │   │       ├── Formatting Test.docx
│   │   │       ├── sample_data.csv
│   │   │       ├── sample_data.xlsx
│   │   │       ├── sample_page.html
│   │   │       ├── sample_presentation.pptx
│   │   │       └── simple_pdf.pdf
│   │   └── uv.lock
│   ├── explorer-assistant
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── chat.py
│   │   │   ├── config.py
│   │   │   ├── helpers.py
│   │   │   ├── response
│   │   │   │   ├── __init__.py
│   │   │   │   ├── model.py
│   │   │   │   ├── response_anthropic.py
│   │   │   │   ├── response_openai.py
│   │   │   │   └── response.py
│   │   │   └── text_includes
│   │   │       └── guardrails_prompt.txt
│   │   ├── assistant.code-workspace
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── guided-conversation-assistant
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── agents
│   │   │   │   ├── guided_conversation
│   │   │   │   │   ├── config.py
│   │   │   │   │   ├── definition.py
│   │   │   │   │   └── definitions
│   │   │   │   │       ├── er_triage.py
│   │   │   │   │       ├── interview.py
│   │   │   │   │       ├── patient_intake.py
│   │   │   │   │       └── poem_feedback.py
│   │   │   │   └── guided_conversation_agent.py
│   │   │   ├── chat.py
│   │   │   ├── config.py
│   │   │   └── text_includes
│   │   │       └── guardrails_prompt.txt
│   │   ├── assistant.code-workspace
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── knowledge-transfer-assistant
│   │   ├── .claude
│   │   │   └── settings.local.json
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── agentic
│   │   │   │   ├── __init__.py
│   │   │   │   ├── analysis.py
│   │   │   │   ├── coordinator_support.py
│   │   │   │   └── team_welcome.py
│   │   │   ├── assets
│   │   │   │   ├── icon-knowledge-transfer.svg
│   │   │   │   └── icon.svg
│   │   │   ├── assistant.py
│   │   │   ├── common.py
│   │   │   ├── config.py
│   │   │   ├── conversation_clients.py
│   │   │   ├── conversation_share_link.py
│   │   │   ├── data.py
│   │   │   ├── domain
│   │   │   │   ├── __init__.py
│   │   │   │   ├── audience_manager.py
│   │   │   │   ├── information_request_manager.py
│   │   │   │   ├── knowledge_brief_manager.py
│   │   │   │   ├── knowledge_digest_manager.py
│   │   │   │   ├── learning_objectives_manager.py
│   │   │   │   └── share_manager.py
│   │   │   ├── files.py
│   │   │   ├── logging.py
│   │   │   ├── notifications.py
│   │   │   ├── respond.py
│   │   │   ├── storage_models.py
│   │   │   ├── storage.py
│   │   │   ├── string_utils.py
│   │   │   ├── text_includes
│   │   │   │   ├── assistant_info.md
│   │   │   │   ├── card_content.md
│   │   │   │   ├── coordinator_instructions.txt
│   │   │   │   ├── coordinator_role.txt
│   │   │   │   ├── knowledge_digest_instructions.txt
│   │   │   │   ├── knowledge_digest_prompt.txt
│   │   │   │   ├── share_information_request_detection.txt
│   │   │   │   ├── team_instructions.txt
│   │   │   │   ├── team_role.txt
│   │   │   │   └── welcome_message_generation.txt
│   │   │   ├── tools
│   │   │   │   ├── __init__.py
│   │   │   │   ├── base.py
│   │   │   │   ├── information_requests.py
│   │   │   │   ├── learning_objectives.py
│   │   │   │   ├── learning_outcomes.py
│   │   │   │   ├── progress_tracking.py
│   │   │   │   └── share_setup.py
│   │   │   ├── ui_tabs
│   │   │   │   ├── __init__.py
│   │   │   │   ├── brief.py
│   │   │   │   ├── common.py
│   │   │   │   ├── debug.py
│   │   │   │   ├── learning.py
│   │   │   │   └── sharing.py
│   │   │   └── utils.py
│   │   ├── CLAUDE.md
│   │   ├── docs
│   │   │   ├── design
│   │   │   │   ├── actions.md
│   │   │   │   └── inference.md
│   │   │   ├── DEV_GUIDE.md
│   │   │   ├── how-kta-works.md
│   │   │   ├── JTBD.md
│   │   │   ├── knowledge-transfer-goals.md
│   │   │   ├── learning_assistance.md
│   │   │   ├── notable_claude_conversations
│   │   │   │   ├── clarifying_quad_modal_design.md
│   │   │   │   ├── CLAUDE_PROMPTS.md
│   │   │   │   ├── transfer_state.md
│   │   │   │   └── trying_the_context_agent.md
│   │   │   └── opportunities-of-knowledge-transfer.md
│   │   ├── knowledge-transfer-assistant.code-workspace
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   ├── __init__.py
│   │   │   ├── test_artifact_loading.py
│   │   │   ├── test_inspector.py
│   │   │   ├── test_share_manager.py
│   │   │   ├── test_share_storage.py
│   │   │   ├── test_share_tools.py
│   │   │   └── test_team_mode.py
│   │   └── uv.lock
│   ├── Makefile
│   ├── navigator-assistant
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── assets
│   │   │   │   ├── card_content.md
│   │   │   │   └── icon.svg
│   │   │   ├── chat.py
│   │   │   ├── config.py
│   │   │   ├── helpers.py
│   │   │   ├── response
│   │   │   │   ├── __init__.py
│   │   │   │   ├── completion_handler.py
│   │   │   │   ├── completion_requestor.py
│   │   │   │   ├── local_tool
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── add_assistant_to_conversation.py
│   │   │   │   │   ├── list_assistant_services.py
│   │   │   │   │   └── model.py
│   │   │   │   ├── models.py
│   │   │   │   ├── prompt.py
│   │   │   │   ├── request_builder.py
│   │   │   │   ├── response.py
│   │   │   │   ├── step_handler.py
│   │   │   │   └── utils
│   │   │   │       ├── __init__.py
│   │   │   │       ├── formatting_utils.py
│   │   │   │       ├── message_utils.py
│   │   │   │       ├── openai_utils.py
│   │   │   │       └── tools.py
│   │   │   ├── text_includes
│   │   │   │   ├── guardrails_prompt.md
│   │   │   │   ├── guidance_prompt.md
│   │   │   │   ├── instruction_prompt.md
│   │   │   │   ├── navigator_assistant_info.md
│   │   │   │   └── semantic_workbench_features.md
│   │   │   └── whiteboard
│   │   │       ├── __init__.py
│   │   │       ├── _inspector.py
│   │   │       └── _whiteboard.py
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── project-assistant
│   │   ├── .cspell
│   │   │   └── custom-dictionary-workspace.txt
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── agentic
│   │   │   │   ├── __init__.py
│   │   │   │   ├── act.py
│   │   │   │   ├── coordinator_next_action.py
│   │   │   │   ├── create_invitation.py
│   │   │   │   ├── detect_audience_and_takeaways.py
│   │   │   │   ├── detect_coordinator_actions.py
│   │   │   │   ├── detect_information_request_needs.py
│   │   │   │   ├── detect_knowledge_package_gaps.py
│   │   │   │   ├── focus.py
│   │   │   │   ├── respond.py
│   │   │   │   ├── team_welcome.py
│   │   │   │   └── update_digest.py
│   │   │   ├── assets
│   │   │   │   ├── icon-knowledge-transfer.svg
│   │   │   │   └── icon.svg
│   │   │   ├── assistant.py
│   │   │   ├── common.py
│   │   │   ├── config.py
│   │   │   ├── conversation_clients.py
│   │   │   ├── data.py
│   │   │   ├── domain
│   │   │   │   ├── __init__.py
│   │   │   │   ├── audience_manager.py
│   │   │   │   ├── conversation_preferences_manager.py
│   │   │   │   ├── information_request_manager.py
│   │   │   │   ├── knowledge_brief_manager.py
│   │   │   │   ├── knowledge_digest_manager.py
│   │   │   │   ├── learning_objectives_manager.py
│   │   │   │   ├── share_manager.py
│   │   │   │   ├── tasks_manager.py
│   │   │   │   └── transfer_manager.py
│   │   │   ├── errors.py
│   │   │   ├── files.py
│   │   │   ├── logging.py
│   │   │   ├── notifications.py
│   │   │   ├── prompt_utils.py
│   │   │   ├── storage.py
│   │   │   ├── string_utils.py
│   │   │   ├── text_includes
│   │   │   │   ├── actor_instructions.md
│   │   │   │   ├── assistant_info.md
│   │   │   │   ├── card_content.md
│   │   │   │   ├── coordinator_instructions copy.md
│   │   │   │   ├── coordinator_instructions.md
│   │   │   │   ├── create_invitation.md
│   │   │   │   ├── detect_audience.md
│   │   │   │   ├── detect_coordinator_actions.md
│   │   │   │   ├── detect_information_request_needs.md
│   │   │   │   ├── detect_knowledge_package_gaps.md
│   │   │   │   ├── focus.md
│   │   │   │   ├── knowledge_digest_instructions.txt
│   │   │   │   ├── team_instructions.txt
│   │   │   │   ├── to_do.md
│   │   │   │   ├── update_knowledge_brief.md
│   │   │   │   ├── update_knowledge_digest.md
│   │   │   │   └── welcome_message_generation.txt
│   │   │   ├── tools
│   │   │   │   ├── __init__.py
│   │   │   │   ├── base.py
│   │   │   │   ├── conversation_preferences.py
│   │   │   │   ├── information_requests.py
│   │   │   │   ├── learning_objectives.py
│   │   │   │   ├── learning_outcomes.py
│   │   │   │   ├── progress_tracking.py
│   │   │   │   ├── share_setup.py
│   │   │   │   ├── system_reminders.py
│   │   │   │   ├── tasks.py
│   │   │   │   └── todo.py
│   │   │   ├── ui_tabs
│   │   │   │   ├── __init__.py
│   │   │   │   ├── brief.py
│   │   │   │   ├── common.py
│   │   │   │   ├── debug.py
│   │   │   │   ├── learning.py
│   │   │   │   └── sharing.py
│   │   │   └── utils.py
│   │   ├── CLAUDE.md
│   │   ├── docs
│   │   │   ├── design
│   │   │   │   ├── actions.md
│   │   │   │   ├── control_options.md
│   │   │   │   ├── design.md
│   │   │   │   ├── inference.md
│   │   │   │   └── PXL_20250814_190140267.jpg
│   │   │   ├── DEV_GUIDE.md
│   │   │   ├── how-kta-works.md
│   │   │   ├── JTBD.md
│   │   │   ├── knowledge-transfer-goals.md
│   │   │   ├── learning_assistance.md
│   │   │   ├── notable_claude_conversations
│   │   │   │   ├── clarifying_quad_modal_design.md
│   │   │   │   ├── CLAUDE_PROMPTS.md
│   │   │   │   ├── transfer_state.md
│   │   │   │   └── trying_the_context_agent.md
│   │   │   └── opportunities-of-knowledge-transfer.md
│   │   ├── knowledge-transfer-assistant.code-workspace
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   ├── __init__.py
│   │   │   ├── test_artifact_loading.py
│   │   │   ├── test_inspector.py
│   │   │   ├── test_share_manager.py
│   │   │   ├── test_share_storage.py
│   │   │   └── test_team_mode.py
│   │   └── uv.lock
│   ├── prospector-assistant
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── agents
│   │   │   │   ├── artifact_agent.py
│   │   │   │   ├── document
│   │   │   │   │   ├── config.py
│   │   │   │   │   ├── gc_draft_content_feedback_config.py
│   │   │   │   │   ├── gc_draft_outline_feedback_config.py
│   │   │   │   │   ├── guided_conversation.py
│   │   │   │   │   └── state.py
│   │   │   │   └── document_agent.py
│   │   │   ├── artifact_creation_extension
│   │   │   │   ├── __init__.py
│   │   │   │   ├── _llm.py
│   │   │   │   ├── config.py
│   │   │   │   ├── document.py
│   │   │   │   ├── extension.py
│   │   │   │   ├── store.py
│   │   │   │   ├── test
│   │   │   │   │   ├── conftest.py
│   │   │   │   │   ├── evaluation.py
│   │   │   │   │   ├── test_completion_with_tools.py
│   │   │   │   │   └── test_extension.py
│   │   │   │   └── tools.py
│   │   │   ├── chat.py
│   │   │   ├── config.py
│   │   │   ├── form_fill_extension
│   │   │   │   ├── __init__.py
│   │   │   │   ├── config.py
│   │   │   │   ├── extension.py
│   │   │   │   ├── inspector.py
│   │   │   │   ├── state.py
│   │   │   │   └── steps
│   │   │   │       ├── __init__.py
│   │   │   │       ├── _guided_conversation.py
│   │   │   │       ├── _llm.py
│   │   │   │       ├── acquire_form_step.py
│   │   │   │       ├── extract_form_fields_step.py
│   │   │   │       ├── fill_form_step.py
│   │   │   │       └── types.py
│   │   │   ├── helpers.py
│   │   │   ├── legacy.py
│   │   │   └── text_includes
│   │   │       ├── artifact_agent_enabled.md
│   │   │       ├── guardrails_prompt.txt
│   │   │       ├── guided_conversation_agent_enabled.md
│   │   │       └── skills_agent_enabled.md
│   │   ├── assistant.code-workspace
│   │   ├── gc_learnings
│   │   │   ├── gc_learnings.md
│   │   │   └── images
│   │   │       ├── gc_conversation_plan_fcn.png
│   │   │       ├── gc_conversation_plan_template.png
│   │   │       ├── gc_execute_plan_callstack.png
│   │   │       ├── gc_functions.png
│   │   │       ├── gc_generate_plan_callstack.png
│   │   │       ├── gc_get_resource_instructions.png
│   │   │       ├── gc_get_termination_instructions.png
│   │   │       ├── gc_kernel_arguments.png
│   │   │       ├── gc_plan_calls.png
│   │   │       ├── gc_termination_instructions.png
│   │   │       ├── sk_get_chat_message_contents.png
│   │   │       ├── sk_inner_get_chat_message_contents.png
│   │   │       ├── sk_send_request_prep.png
│   │   │       └── sk_send_request.png
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   └── skill-assistant
│       ├── .env.example
│       ├── .vscode
│       │   ├── launch.json
│       │   └── settings.json
│       ├── assistant
│       │   ├── __init__.py
│       │   ├── config.py
│       │   ├── logging.py
│       │   ├── skill_assistant.py
│       │   ├── skill_engine_registry.py
│       │   ├── skill_event_mapper.py
│       │   ├── text_includes
│       │   │   └── guardrails_prompt.txt
│       │   └── workbench_helpers.py
│       ├── assistant.code-workspace
│       ├── Makefile
│       ├── pyproject.toml
│       ├── README.md
│       ├── tests
│       │   └── test_setup.py
│       └── uv.lock
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── docs
│   ├── .vscode
│   │   └── settings.json
│   ├── ASSISTANT_CONFIG.md
│   ├── ASSISTANT_DEVELOPMENT_GUIDE.md
│   ├── CUSTOM_APP_REGISTRATION.md
│   ├── HOSTED_ASSISTANT_WITH_LOCAL_MCP_SERVERS.md
│   ├── images
│   │   ├── architecture-animation.gif
│   │   ├── configure_assistant.png
│   │   ├── conversation_canvas_open.png
│   │   ├── conversation_duplicate.png
│   │   ├── conversation_export.png
│   │   ├── conversation_share_dialog.png
│   │   ├── conversation_share_link.png
│   │   ├── dashboard_configured_view.png
│   │   ├── dashboard_view.png
│   │   ├── license_agreement.png
│   │   ├── message_bar.png
│   │   ├── message_inspection.png
│   │   ├── message_link.png
│   │   ├── new_prospector_assistant_dialog.png
│   │   ├── open_conversation_canvas.png
│   │   ├── prospector_example.png
│   │   ├── readme1.png
│   │   ├── readme2.png
│   │   ├── readme3.png
│   │   ├── rewind.png
│   │   ├── signin_page.png
│   │   └── splash_screen.png
│   ├── LOCAL_ASSISTANT_WITH_REMOTE_WORKBENCH.md
│   ├── SETUP_DEV_ENVIRONMENT.md
│   └── WORKBENCH_APP.md
├── examples
│   ├── dotnet
│   │   ├── .editorconfig
│   │   ├── dotnet-01-echo-bot
│   │   │   ├── appsettings.json
│   │   │   ├── dotnet-01-echo-bot.csproj
│   │   │   ├── MyAgent.cs
│   │   │   ├── MyAgentConfig.cs
│   │   │   ├── MyWorkbenchConnector.cs
│   │   │   ├── Program.cs
│   │   │   └── README.md
│   │   ├── dotnet-02-message-types-demo
│   │   │   ├── appsettings.json
│   │   │   ├── ConnectorExtensions.cs
│   │   │   ├── docs
│   │   │   │   ├── abc.png
│   │   │   │   ├── code.png
│   │   │   │   ├── config.png
│   │   │   │   ├── echo.png
│   │   │   │   ├── markdown.png
│   │   │   │   ├── mermaid.png
│   │   │   │   ├── reverse.png
│   │   │   │   └── safety-check.png
│   │   │   ├── dotnet-02-message-types-demo.csproj
│   │   │   ├── MyAgent.cs
│   │   │   ├── MyAgentConfig.cs
│   │   │   ├── MyWorkbenchConnector.cs
│   │   │   ├── Program.cs
│   │   │   └── README.md
│   │   └── dotnet-03-simple-chatbot
│   │       ├── appsettings.json
│   │       ├── ConnectorExtensions.cs
│   │       ├── dotnet-03-simple-chatbot.csproj
│   │       ├── MyAgent.cs
│   │       ├── MyAgentConfig.cs
│   │       ├── MyWorkbenchConnector.cs
│   │       ├── Program.cs
│   │       └── README.md
│   ├── Makefile
│   └── python
│       ├── python-01-echo-bot
│       │   ├── .env.example
│       │   ├── .vscode
│       │   │   ├── launch.json
│       │   │   └── settings.json
│       │   ├── assistant
│       │   │   ├── __init__.py
│       │   │   ├── chat.py
│       │   │   └── config.py
│       │   ├── assistant.code-workspace
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── python-02-simple-chatbot
│       │   ├── .env.example
│       │   ├── .vscode
│       │   │   ├── launch.json
│       │   │   └── settings.json
│       │   ├── assistant
│       │   │   ├── __init__.py
│       │   │   ├── chat.py
│       │   │   ├── config.py
│       │   │   └── text_includes
│       │   │       └── guardrails_prompt.txt
│       │   ├── assistant.code-workspace
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       └── python-03-multimodel-chatbot
│           ├── .env.example
│           ├── .vscode
│           │   ├── launch.json
│           │   └── settings.json
│           ├── assistant
│           │   ├── __init__.py
│           │   ├── chat.py
│           │   ├── config.py
│           │   ├── model_adapters.py
│           │   └── text_includes
│           │       └── guardrails_prompt.txt
│           ├── assistant.code-workspace
│           ├── Makefile
│           ├── pyproject.toml
│           ├── README.md
│           └── uv.lock
├── KNOWN_ISSUES.md
├── libraries
│   ├── dotnet
│   │   ├── .editorconfig
│   │   ├── pack.sh
│   │   ├── README.md
│   │   ├── SemanticWorkbench.sln
│   │   ├── SemanticWorkbench.sln.DotSettings
│   │   └── WorkbenchConnector
│   │       ├── AgentBase.cs
│   │       ├── AgentConfig
│   │       │   ├── AgentConfigBase.cs
│   │       │   ├── AgentConfigPropertyAttribute.cs
│   │       │   └── ConfigUtils.cs
│   │       ├── Constants.cs
│   │       ├── IAgentBase.cs
│   │       ├── icon.png
│   │       ├── Models
│   │       │   ├── Command.cs
│   │       │   ├── Conversation.cs
│   │       │   ├── ConversationEvent.cs
│   │       │   ├── DebugInfo.cs
│   │       │   ├── Insight.cs
│   │       │   ├── Message.cs
│   │       │   ├── MessageMetadata.cs
│   │       │   ├── Participant.cs
│   │       │   ├── Sender.cs
│   │       │   └── ServiceInfo.cs
│   │       ├── Storage
│   │       │   ├── AgentInfo.cs
│   │       │   ├── AgentServiceStorage.cs
│   │       │   └── IAgentServiceStorage.cs
│   │       ├── StringLoggingExtensions.cs
│   │       ├── Webservice.cs
│   │       ├── WorkbenchConfig.cs
│   │       ├── WorkbenchConnector.cs
│   │       └── WorkbenchConnector.csproj
│   ├── Makefile
│   └── python
│       ├── anthropic-client
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── anthropic_client
│       │   │   ├── __init__.py
│       │   │   ├── client.py
│       │   │   ├── config.py
│       │   │   └── messages.py
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── assistant-data-gen
│       │   ├── .vscode
│       │   │   ├── launch.json
│       │   │   └── settings.json
│       │   ├── assistant_data_gen
│       │   │   ├── __init__.py
│       │   │   ├── assistant_api.py
│       │   │   ├── config.py
│       │   │   ├── gce
│       │   │   │   ├── __init__.py
│       │   │   │   ├── gce_agent.py
│       │   │   │   └── prompts.py
│       │   │   └── pydantic_ai_utils.py
│       │   ├── configs
│       │   │   └── document_assistant_example_config.yaml
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── scripts
│       │   │   ├── gce_simulation.py
│       │   │   └── generate_scenario.py
│       │   └── uv.lock
│       ├── assistant-drive
│       │   ├── .gitignore
│       │   ├── .vscode
│       │   │   ├── extensions.json
│       │   │   └── settings.json
│       │   ├── assistant_drive
│       │   │   ├── __init__.py
│       │   │   ├── drive.py
│       │   │   └── tests
│       │   │       └── test_basic.py
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── pytest.ini
│       │   ├── README.md
│       │   ├── usage.ipynb
│       │   └── uv.lock
│       ├── assistant-extensions
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── assistant_extensions
│       │   │   ├── __init__.py
│       │   │   ├── ai_clients
│       │   │   │   └── config.py
│       │   │   ├── artifacts
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _artifacts.py
│       │   │   │   ├── _inspector.py
│       │   │   │   └── _model.py
│       │   │   ├── attachments
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _attachments.py
│       │   │   │   ├── _convert.py
│       │   │   │   ├── _model.py
│       │   │   │   ├── _shared.py
│       │   │   │   └── _summarizer.py
│       │   │   ├── chat_context_toolkit
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _config.py
│       │   │   │   ├── archive
│       │   │   │   │   ├── __init__.py
│       │   │   │   │   ├── _archive.py
│       │   │   │   │   └── _summarizer.py
│       │   │   │   ├── message_history
│       │   │   │   │   ├── __init__.py
│       │   │   │   │   ├── _history.py
│       │   │   │   │   └── _message.py
│       │   │   │   └── virtual_filesystem
│       │   │   │       ├── __init__.py
│       │   │   │       ├── _archive_file_source.py
│       │   │   │       └── _attachments_file_source.py
│       │   │   ├── dashboard_card
│       │   │   │   ├── __init__.py
│       │   │   │   └── _dashboard_card.py
│       │   │   ├── document_editor
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _extension.py
│       │   │   │   ├── _inspector.py
│       │   │   │   └── _model.py
│       │   │   ├── mcp
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _assistant_file_resource_handler.py
│       │   │   │   ├── _client_utils.py
│       │   │   │   ├── _devtunnel.py
│       │   │   │   ├── _model.py
│       │   │   │   ├── _openai_utils.py
│       │   │   │   ├── _sampling_handler.py
│       │   │   │   ├── _tool_utils.py
│       │   │   │   └── _workbench_file_resource_handler.py
│       │   │   ├── navigator
│       │   │   │   ├── __init__.py
│       │   │   │   └── _navigator.py
│       │   │   └── workflows
│       │   │       ├── __init__.py
│       │   │       ├── _model.py
│       │   │       ├── _workflows.py
│       │   │       └── runners
│       │   │           └── _user_proxy.py
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── test
│       │   │   └── attachments
│       │   │       └── test_attachments.py
│       │   └── uv.lock
│       ├── chat-context-toolkit
│       │   ├── .claude
│       │   │   └── settings.local.json
│       │   ├── .env.sample
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── assets
│       │   │   ├── archive_v1.png
│       │   │   ├── history_v1.png
│       │   │   └── vfs_v1.png
│       │   ├── chat_context_toolkit
│       │   │   ├── __init__.py
│       │   │   ├── archive
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _archive_reader.py
│       │   │   │   ├── _archive_task_queue.py
│       │   │   │   ├── _state.py
│       │   │   │   ├── _types.py
│       │   │   │   └── summarization
│       │   │   │       ├── __init__.py
│       │   │   │       └── _summarizer.py
│       │   │   ├── history
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _budget.py
│       │   │   │   ├── _decorators.py
│       │   │   │   ├── _history.py
│       │   │   │   ├── _prioritize.py
│       │   │   │   ├── _types.py
│       │   │   │   └── tool_abbreviations
│       │   │   │       ├── __init__.py
│       │   │   │       └── _tool_abbreviations.py
│       │   │   └── virtual_filesystem
│       │   │       ├── __init__.py
│       │   │       ├── _types.py
│       │   │       ├── _virtual_filesystem.py
│       │   │       ├── README.md
│       │   │       └── tools
│       │   │           ├── __init__.py
│       │   │           ├── _ls_tool.py
│       │   │           ├── _tools.py
│       │   │           └── _view_tool.py
│       │   ├── CLAUDE.md
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── test
│       │   │   ├── archive
│       │   │   │   └── test_archive_reader.py
│       │   │   ├── history
│       │   │   │   ├── test_abbreviate_messages.py
│       │   │   │   ├── test_history.py
│       │   │   │   ├── test_pair_and_order_tool_messages.py
│       │   │   │   ├── test_prioritize.py
│       │   │   │   └── test_truncate_messages.py
│       │   │   └── virtual_filesystem
│       │   │       ├── test_virtual_filesystem.py
│       │   │       └── tools
│       │   │           ├── test_ls_tool.py
│       │   │           ├── test_tools.py
│       │   │           └── test_view_tool.py
│       │   └── uv.lock
│       ├── content-safety
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── content_safety
│       │   │   ├── __init__.py
│       │   │   ├── evaluators
│       │   │   │   ├── __init__.py
│       │   │   │   ├── azure_content_safety
│       │   │   │   │   ├── __init__.py
│       │   │   │   │   ├── config.py
│       │   │   │   │   └── evaluator.py
│       │   │   │   ├── config.py
│       │   │   │   ├── evaluator.py
│       │   │   │   └── openai_moderations
│       │   │   │       ├── __init__.py
│       │   │   │       ├── config.py
│       │   │   │       └── evaluator.py
│       │   │   └── README.md
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── events
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── events
│       │   │   ├── __init__.py
│       │   │   └── events.py
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── guided-conversation
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── guided_conversation
│       │   │   ├── __init__.py
│       │   │   ├── functions
│       │   │   │   ├── __init__.py
│       │   │   │   ├── conversation_plan.py
│       │   │   │   ├── execution.py
│       │   │   │   └── final_update_plan.py
│       │   │   ├── guided_conversation_agent.py
│       │   │   ├── plugins
│       │   │   │   ├── __init__.py
│       │   │   │   ├── agenda.py
│       │   │   │   └── artifact.py
│       │   │   └── utils
│       │   │       ├── __init__.py
│       │   │       ├── base_model_llm.py
│       │   │       ├── conversation_helpers.py
│       │   │       ├── openai_tool_calling.py
│       │   │       ├── plugin_helpers.py
│       │   │       └── resources.py
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── llm-client
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── llm_client
│       │   │   ├── __init__.py
│       │   │   └── model.py
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── Makefile
│       ├── mcp-extensions
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── Makefile
│       │   ├── mcp_extensions
│       │   │   ├── __init__.py
│       │   │   ├── _client_session.py
│       │   │   ├── _model.py
│       │   │   ├── _sampling.py
│       │   │   ├── _server_extensions.py
│       │   │   ├── _tool_utils.py
│       │   │   ├── llm
│       │   │   │   ├── __init__.py
│       │   │   │   ├── chat_completion.py
│       │   │   │   ├── helpers.py
│       │   │   │   ├── llm_types.py
│       │   │   │   ├── mcp_chat_completion.py
│       │   │   │   └── openai_chat_completion.py
│       │   │   └── server
│       │   │       ├── __init__.py
│       │   │       └── storage.py
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── tests
│       │   │   └── test_tool_utils.py
│       │   └── uv.lock
│       ├── mcp-tunnel
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── Makefile
│       │   ├── mcp_tunnel
│       │   │   ├── __init__.py
│       │   │   ├── _devtunnel.py
│       │   │   ├── _dir.py
│       │   │   └── _main.py
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── openai-client
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── Makefile
│       │   ├── openai_client
│       │   │   ├── __init__.py
│       │   │   ├── chat_driver
│       │   │   │   ├── __init__.py
│       │   │   │   ├── chat_driver.ipynb
│       │   │   │   ├── chat_driver.py
│       │   │   │   ├── message_history_providers
│       │   │   │   │   ├── __init__.py
│       │   │   │   │   ├── in_memory_message_history_provider.py
│       │   │   │   │   ├── local_message_history_provider.py
│       │   │   │   │   ├── message_history_provider.py
│       │   │   │   │   └── tests
│       │   │   │   │       └── formatted_instructions_test.py
│       │   │   │   └── README.md
│       │   │   ├── client.py
│       │   │   ├── completion.py
│       │   │   ├── config.py
│       │   │   ├── errors.py
│       │   │   ├── logging.py
│       │   │   ├── messages.py
│       │   │   ├── tokens.py
│       │   │   └── tools.py
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── tests
│       │   │   ├── test_command_parsing.py
│       │   │   ├── test_formatted_messages.py
│       │   │   ├── test_messages.py
│       │   │   └── test_tokens.py
│       │   └── uv.lock
│       ├── semantic-workbench-api-model
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── semantic_workbench_api_model
│       │   │   ├── __init__.py
│       │   │   ├── assistant_model.py
│       │   │   ├── assistant_service_client.py
│       │   │   ├── workbench_model.py
│       │   │   └── workbench_service_client.py
│       │   └── uv.lock
│       ├── semantic-workbench-assistant
│       │   ├── .vscode
│       │   │   ├── launch.json
│       │   │   └── settings.json
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── semantic_workbench_assistant
│       │   │   ├── __init__.py
│       │   │   ├── assistant_app
│       │   │   │   ├── __init__.py
│       │   │   │   ├── assistant.py
│       │   │   │   ├── config.py
│       │   │   │   ├── content_safety.py
│       │   │   │   ├── context.py
│       │   │   │   ├── error.py
│       │   │   │   ├── export_import.py
│       │   │   │   ├── protocol.py
│       │   │   │   └── service.py
│       │   │   ├── assistant_service.py
│       │   │   ├── auth.py
│       │   │   ├── canonical.py
│       │   │   ├── command.py
│       │   │   ├── config.py
│       │   │   ├── logging_config.py
│       │   │   ├── settings.py
│       │   │   ├── start.py
│       │   │   └── storage.py
│       │   ├── tests
│       │   │   ├── conftest.py
│       │   │   ├── test_assistant_app.py
│       │   │   ├── test_canonical.py
│       │   │   ├── test_config.py
│       │   │   └── test_storage.py
│       │   └── uv.lock
│       └── skills
│           ├── .vscode
│           │   └── settings.json
│           ├── Makefile
│           ├── README.md
│           └── skill-library
│               ├── .vscode
│               │   └── settings.json
│               ├── docs
│               │   └── vs-recipe-tool.md
│               ├── Makefile
│               ├── pyproject.toml
│               ├── README.md
│               ├── skill_library
│               │   ├── __init__.py
│               │   ├── chat_driver_helpers.py
│               │   ├── cli
│               │   │   ├── azure_openai.py
│               │   │   ├── conversation_history.py
│               │   │   ├── README.md
│               │   │   ├── run_routine.py
│               │   │   ├── settings.py
│               │   │   └── skill_logger.py
│               │   ├── engine.py
│               │   ├── llm_info.txt
│               │   ├── logging.py
│               │   ├── README.md
│               │   ├── routine_stack.py
│               │   ├── skill.py
│               │   ├── skills
│               │   │   ├── common
│               │   │   │   ├── __init__.py
│               │   │   │   ├── common_skill.py
│               │   │   │   └── routines
│               │   │   │       ├── bing_search.py
│               │   │   │       ├── consolidate.py
│               │   │   │       ├── echo.py
│               │   │   │       ├── gather_context.py
│               │   │   │       ├── get_content_from_url.py
│               │   │   │       ├── gpt_complete.py
│               │   │   │       ├── select_user_intent.py
│               │   │   │       └── summarize.py
│               │   │   ├── eval
│               │   │   │   ├── __init__.py
│               │   │   │   ├── eval_skill.py
│               │   │   │   └── routines
│               │   │   │       └── eval.py
│               │   │   ├── fabric
│               │   │   │   ├── __init__.py
│               │   │   │   ├── fabric_skill.py
│               │   │   │   ├── patterns
│               │   │   │   │   ├── agility_story
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── ai
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_answers
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_candidates
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_cfp_submission
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_claims
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_comments
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_debate
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_email_headers
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_incident
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_interviewer_techniques
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_logs
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_malware
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_military_strategy
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_mistakes
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_paper
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_patent
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_personality
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_presentation
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_product_feedback
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_proposition
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_prose
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_prose_json
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_prose_pinker
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_risk
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_sales_call
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_spiritual_text
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_tech_impact
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_threat_report
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_threat_report_cmds
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_threat_report_trends
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── answer_interview_question
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── ask_secure_by_design_questions
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── ask_uncle_duke
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── capture_thinkers_work
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── check_agreement
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── clean_text
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── coding_master
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── compare_and_contrast
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── convert_to_markdown
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_5_sentence_summary
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_academic_paper
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_ai_jobs_analysis
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_aphorisms
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_art_prompt
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_better_frame
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_coding_project
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_command
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_cyber_summary
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_design_document
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_diy
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_formal_email
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_git_diff_commit
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_graph_from_input
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_hormozi_offer
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_idea_compass
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_investigation_visualization
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_keynote
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_logo
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_markmap_visualization
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_mermaid_visualization
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_mermaid_visualization_for_github
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_micro_summary
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_network_threat_landscape
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_newsletter_entry
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_npc
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_pattern
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_prd
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_prediction_block
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_quiz
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_reading_plan
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_recursive_outline
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_report_finding
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_rpg_summary
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_security_update
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_show_intro
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_sigma_rules
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_story_explanation
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_stride_threat_model
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_summary
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_tags
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_threat_scenarios
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_ttrc_graph
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_ttrc_narrative
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_upgrade_pack
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_user_story
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_video_chapters
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_visualization
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── dialog_with_socrates
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── enrich_blog_post
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── explain_code
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── explain_docs
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── explain_math
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── explain_project
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── explain_terms
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── export_data_as_csv
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_algorithm_update_recommendations
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── extract_article_wisdom
│               │   │   │   │   │   ├── dmiessler
│               │   │   │   │   │   │   └── extract_wisdom-1.0.0
│               │   │   │   │   │   │       ├── system.md
│               │   │   │   │   │   │       └── user.md
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── extract_book_ideas
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_book_recommendations
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_business_ideas
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_controversial_ideas
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_core_message
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_ctf_writeup
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_domains
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_extraordinary_claims
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_ideas
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_insights
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_insights_dm
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_instructions
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_jokes
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_latest_video
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_main_idea
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_most_redeeming_thing
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_patterns
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_poc
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── extract_predictions
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_primary_problem
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_primary_solution
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_product_features
│               │   │   │   │   │   ├── dmiessler
│               │   │   │   │   │   │   └── extract_wisdom-1.0.0
│               │   │   │   │   │   │       ├── system.md
│               │   │   │   │   │   │       └── user.md
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_questions
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_recipe
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_recommendations
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── extract_references
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── extract_skills
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_song_meaning
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_sponsors
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_videoid
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── extract_wisdom
│               │   │   │   │   │   ├── dmiessler
│               │   │   │   │   │   │   └── extract_wisdom-1.0.0
│               │   │   │   │   │   │       ├── system.md
│               │   │   │   │   │   │       └── user.md
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_wisdom_agents
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_wisdom_dm
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_wisdom_nometa
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── find_hidden_message
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── find_logical_fallacies
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── get_wow_per_minute
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── get_youtube_rss
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── humanize
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── identify_dsrp_distinctions
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── identify_dsrp_perspectives
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── identify_dsrp_relationships
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── identify_dsrp_systems
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── identify_job_stories
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── improve_academic_writing
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── improve_prompt
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── improve_report_finding
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── improve_writing
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── judge_output
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── label_and_rate
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── loaded
│               │   │   │   │   ├── md_callout
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── official_pattern_template
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── pattern_explanations.md
│               │   │   │   │   ├── prepare_7s_strategy
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── provide_guidance
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── rate_ai_response
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── rate_ai_result
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── rate_content
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── rate_value
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── raw_query
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── raycast
│               │   │   │   │   │   ├── capture_thinkers_work
│               │   │   │   │   │   ├── create_story_explanation
│               │   │   │   │   │   ├── extract_primary_problem
│               │   │   │   │   │   ├── extract_wisdom
│               │   │   │   │   │   └── yt
│               │   │   │   │   ├── recommend_artists
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── recommend_pipeline_upgrades
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── recommend_talkpanel_topics
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── refine_design_document
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── review_design
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── sanitize_broken_html_to_markdown
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── show_fabric_options_markmap
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── solve_with_cot
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── stringify
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── suggest_pattern
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── summarize
│               │   │   │   │   │   ├── dmiessler
│               │   │   │   │   │   │   └── summarize
│               │   │   │   │   │   │       ├── system.md
│               │   │   │   │   │   │       └── user.md
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── summarize_debate
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_git_changes
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_git_diff
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_lecture
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_legislation
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_meeting
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_micro
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── summarize_newsletter
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── summarize_paper
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── summarize_prompt
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_pull-requests
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── summarize_rpg_session
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_analyze_challenge_handling
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_check_metrics
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_create_h3_career
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_create_opening_sentences
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_describe_life_outlook
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_extract_intro_sentences
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_extract_panel_topics
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_find_blindspots
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_find_negative_thinking
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_find_neglected_goals
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_give_encouragement
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_red_team_thinking
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_threat_model_plans
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_visualize_mission_goals_projects
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_year_in_review
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── to_flashcards
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── transcribe_minutes
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── translate
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── tweet
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── write_essay
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── write_hackerone_report
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── write_latex
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── write_micro_essay
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── write_nuclei_template_rule
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── write_pull-request
│               │   │   │   │   │   └── system.md
│               │   │   │   │   └── write_semgrep_rule
│               │   │   │   │       ├── system.md
│               │   │   │   │       └── user.md
│               │   │   │   └── routines
│               │   │   │       ├── list.py
│               │   │   │       ├── run.py
│               │   │   │       └── show.py
│               │   │   ├── guided_conversation
│               │   │   │   ├── __init__.py
│               │   │   │   ├── agenda.py
│               │   │   │   ├── artifact_helpers.py
│               │   │   │   ├── chat_completions
│               │   │   │   │   ├── fix_agenda_error.py
│               │   │   │   │   ├── fix_artifact_error.py
│               │   │   │   │   ├── generate_agenda.py
│               │   │   │   │   ├── generate_artifact_updates.py
│               │   │   │   │   ├── generate_final_artifact.py
│               │   │   │   │   └── generate_message.py
│               │   │   │   ├── conversation_guides
│               │   │   │   │   ├── __init__.py
│               │   │   │   │   ├── acrostic_poem.py
│               │   │   │   │   ├── er_triage.py
│               │   │   │   │   ├── interview.py
│               │   │   │   │   └── patient_intake.py
│               │   │   │   ├── guide.py
│               │   │   │   ├── guided_conversation_skill.py
│               │   │   │   ├── logging.py
│               │   │   │   ├── message.py
│               │   │   │   ├── resources.py
│               │   │   │   ├── routines
│               │   │   │   │   └── guided_conversation.py
│               │   │   │   └── tests
│               │   │   │       ├── conftest.py
│               │   │   │       ├── test_artifact_helpers.py
│               │   │   │       ├── test_generate_agenda.py
│               │   │   │       ├── test_generate_artifact_updates.py
│               │   │   │       ├── test_generate_final_artifact.py
│               │   │   │       └── test_resource.py
│               │   │   ├── meta
│               │   │   │   ├── __init__.py
│               │   │   │   ├── meta_skill.py
│               │   │   │   ├── README.md
│               │   │   │   └── routines
│               │   │   │       └── generate_routine.py
│               │   │   ├── posix
│               │   │   │   ├── __init__.py
│               │   │   │   ├── posix_skill.py
│               │   │   │   ├── routines
│               │   │   │   │   ├── append_file.py
│               │   │   │   │   ├── cd.py
│               │   │   │   │   ├── ls.py
│               │   │   │   │   ├── make_home_dir.py
│               │   │   │   │   ├── mkdir.py
│               │   │   │   │   ├── mv.py
│               │   │   │   │   ├── pwd.py
│               │   │   │   │   ├── read_file.py
│               │   │   │   │   ├── rm.py
│               │   │   │   │   ├── touch.py
│               │   │   │   │   └── write_file.py
│               │   │   │   └── sandbox_shell.py
│               │   │   ├── README.md
│               │   │   ├── research
│               │   │   │   ├── __init__.py
│               │   │   │   ├── README.md
│               │   │   │   ├── research_skill.py
│               │   │   │   └── routines
│               │   │   │       ├── answer_question_about_content.py
│               │   │   │       ├── evaluate_answer.py
│               │   │   │       ├── generate_research_plan.py
│               │   │   │       ├── generate_search_query.py
│               │   │   │       ├── update_research_plan.py
│               │   │   │       ├── web_research.py
│               │   │   │       └── web_search.py
│               │   │   ├── research2
│               │   │   │   ├── __init__.py
│               │   │   │   ├── README.md
│               │   │   │   ├── research_skill.py
│               │   │   │   └── routines
│               │   │   │       ├── facts.py
│               │   │   │       ├── make_final_report.py
│               │   │   │       ├── research.py
│               │   │   │       ├── search_plan.py
│               │   │   │       ├── search.py
│               │   │   │       └── visit_pages.py
│               │   │   └── web_research
│               │   │       ├── __init__.py
│               │   │       ├── README.md
│               │   │       ├── research_skill.py
│               │   │       └── routines
│               │   │           ├── facts.py
│               │   │           ├── make_final_report.py
│               │   │           ├── research.py
│               │   │           ├── search_plan.py
│               │   │           ├── search.py
│               │   │           └── visit_pages.py
│               │   ├── tests
│               │   │   ├── test_common_skill.py
│               │   │   ├── test_integration.py
│               │   │   ├── test_routine_stack.py
│               │   │   ├── tst_skill
│               │   │   │   ├── __init__.py
│               │   │   │   └── routines
│               │   │   │       ├── __init__.py
│               │   │   │       └── a_routine.py
│               │   │   └── utilities
│               │   │       ├── test_find_template_vars.py
│               │   │       ├── test_make_arg_set.py
│               │   │       ├── test_paramspec.py
│               │   │       ├── test_parse_command_string.py
│               │   │       └── test_to_string.py
│               │   ├── types.py
│               │   ├── usage.py
│               │   └── utilities.py
│               └── uv.lock
├── LICENSE
├── Makefile
├── mcp-servers
│   ├── ai-assist-content
│   │   ├── .vscode
│   │   │   └── settings.json
│   │   ├── mcp-example-brave-search.md
│   │   ├── mcp-fastmcp-typescript-README.md
│   │   ├── mcp-llms-full.txt
│   │   ├── mcp-metadata-tips.md
│   │   ├── mcp-python-sdk-README.md
│   │   ├── mcp-typescript-sdk-README.md
│   │   ├── pydanticai-documentation.md
│   │   ├── pydanticai-example-question-graph.md
│   │   ├── pydanticai-example-weather.md
│   │   ├── pydanticai-tutorial.md
│   │   └── README.md
│   ├── Makefile
│   ├── mcp-server-bing-search
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server_bing_search
│   │   │   ├── __init__.py
│   │   │   ├── config.py
│   │   │   ├── prompts
│   │   │   │   ├── __init__.py
│   │   │   │   ├── clean_website.py
│   │   │   │   └── filter_links.py
│   │   │   ├── server.py
│   │   │   ├── start.py
│   │   │   ├── tools.py
│   │   │   ├── types.py
│   │   │   ├── utils.py
│   │   │   └── web
│   │   │       ├── __init__.py
│   │   │       ├── get_content.py
│   │   │       ├── llm_processing.py
│   │   │       ├── process_website.py
│   │   │       └── search_bing.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   └── test_tools.py
│   │   └── uv.lock
│   ├── mcp-server-bundle
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server_bundle
│   │   │   ├── __init__.py
│   │   │   └── main.py
│   │   ├── pyinstaller.spec
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── mcp-server-filesystem
│   │   ├── .env.example
│   │   ├── .github
│   │   │   └── workflows
│   │   │       └── ci.yml
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server_filesystem
│   │   │   ├── __init__.py
│   │   │   ├── config.py
│   │   │   ├── server.py
│   │   │   └── start.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   └── test_filesystem.py
│   │   └── uv.lock
│   ├── mcp-server-filesystem-edit
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── data
│   │   │   ├── attachments
│   │   │   │   ├── Daily Game Ideas.txt
│   │   │   │   ├── Frontend Framework Proposal.txt
│   │   │   │   ├── ReDoodle.txt
│   │   │   │   └── Research Template.tex
│   │   │   ├── test_cases.yaml
│   │   │   └── transcripts
│   │   │       ├── transcript_research_simple.md
│   │   │       ├── transcript_Startup_Idea_1_202503031513.md
│   │   │       ├── transcript_Startup_Idea_2_202503031659.md
│   │   │       └── transcript_Web_Frontends_202502281551.md
│   │   ├── Makefile
│   │   ├── mcp_server_filesystem_edit
│   │   │   ├── __init__.py
│   │   │   ├── app_handling
│   │   │   │   ├── __init__.py
│   │   │   │   ├── excel.py
│   │   │   │   ├── miktex.py
│   │   │   │   ├── office_common.py
│   │   │   │   ├── powerpoint.py
│   │   │   │   └── word.py
│   │   │   ├── config.py
│   │   │   ├── evals
│   │   │   │   ├── __init__.py
│   │   │   │   ├── common.py
│   │   │   │   ├── run_comments.py
│   │   │   │   ├── run_edit.py
│   │   │   │   └── run_ppt_edit.py
│   │   │   ├── prompts
│   │   │   │   ├── __init__.py
│   │   │   │   ├── add_comments.py
│   │   │   │   ├── analyze_comments.py
│   │   │   │   ├── latex_edit.py
│   │   │   │   ├── markdown_draft.py
│   │   │   │   ├── markdown_edit.py
│   │   │   │   └── powerpoint_edit.py
│   │   │   ├── server.py
│   │   │   ├── start.py
│   │   │   ├── tools
│   │   │   │   ├── __init__.py
│   │   │   │   ├── add_comments.py
│   │   │   │   ├── edit_adapters
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── common.py
│   │   │   │   │   ├── latex.py
│   │   │   │   │   └── markdown.py
│   │   │   │   ├── edit.py
│   │   │   │   └── helpers.py
│   │   │   └── types.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   ├── app_handling
│   │   │   │   ├── test_excel.py
│   │   │   │   ├── test_miktext.py
│   │   │   │   ├── test_office_common.py
│   │   │   │   ├── test_powerpoint.py
│   │   │   │   └── test_word.py
│   │   │   ├── conftest.py
│   │   │   └── tools
│   │   │       └── edit_adapters
│   │   │           ├── test_latex.py
│   │   │           └── test_markdown.py
│   │   └── uv.lock
│   ├── mcp-server-fusion
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── AddInIcon.svg
│   │   ├── config.py
│   │   ├── FusionMCPServerAddIn.manifest
│   │   ├── FusionMCPServerAddIn.py
│   │   ├── mcp_server_fusion
│   │   │   ├── __init__.py
│   │   │   ├── fusion_mcp_server.py
│   │   │   ├── fusion_utils
│   │   │   │   ├── __init__.py
│   │   │   │   ├── event_utils.py
│   │   │   │   ├── general_utils.py
│   │   │   │   └── tool_utils.py
│   │   │   ├── mcp_tools
│   │   │   │   ├── __init__.py
│   │   │   │   ├── fusion_3d_operation.py
│   │   │   │   ├── fusion_geometry.py
│   │   │   │   ├── fusion_pattern.py
│   │   │   │   └── fusion_sketch.py
│   │   │   └── vendor
│   │   │       └── README.md
│   │   ├── README.md
│   │   └── requirements.txt
│   ├── mcp-server-giphy
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server
│   │   │   ├── __init__.py
│   │   │   ├── config.py
│   │   │   ├── giphy_search.py
│   │   │   ├── sampling.py
│   │   │   ├── server.py
│   │   │   ├── start.py
│   │   │   └── utils.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── mcp-server-memory-user-bio
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server_memory_user_bio
│   │   │   ├── __init__.py
│   │   │   ├── config.py
│   │   │   ├── server.py
│   │   │   └── start.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── mcp-server-memory-whiteboard
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server_memory_whiteboard
│   │   │   ├── __init__.py
│   │   │   ├── config.py
│   │   │   ├── server.py
│   │   │   └── start.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── mcp-server-office
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── build.sh
│   │   ├── data
│   │   │   ├── attachments
│   │   │   │   ├── Daily Game Ideas.txt
│   │   │   │   ├── Frontend Framework Proposal.txt
│   │   │   │   └── ReDoodle.txt
│   │   │   └── word
│   │   │       ├── test_cases.yaml
│   │   │       └── transcripts
│   │   │           ├── transcript_Startup_Idea_1_202503031513.md
│   │   │           ├── transcript_Startup_Idea_2_202503031659.md
│   │   │           └── transcript_Web_Frontends_202502281551.md
│   │   ├── Makefile
│   │   ├── mcp_server
│   │   │   ├── __init__.py
│   │   │   ├── app_interaction
│   │   │   │   ├── __init__.py
│   │   │   │   ├── excel_editor.py
│   │   │   │   ├── powerpoint_editor.py
│   │   │   │   └── word_editor.py
│   │   │   ├── config.py
│   │   │   ├── constants.py
│   │   │   ├── evals
│   │   │   │   ├── __init__.py
│   │   │   │   ├── common.py
│   │   │   │   ├── run_comment_analysis.py
│   │   │   │   ├── run_feedback.py
│   │   │   │   └── run_markdown_edit.py
│   │   │   ├── helpers.py
│   │   │   ├── markdown_edit
│   │   │   │   ├── __init__.py
│   │   │   │   ├── comment_analysis.py
│   │   │   │   ├── feedback_step.py
│   │   │   │   ├── markdown_edit.py
│   │   │   │   └── utils.py
│   │   │   ├── prompts
│   │   │   │   ├── __init__.py
│   │   │   │   ├── comment_analysis.py
│   │   │   │   ├── feedback.py
│   │   │   │   ├── markdown_draft.py
│   │   │   │   └── markdown_edit.py
│   │   │   ├── server.py
│   │   │   ├── start.py
│   │   │   └── types.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   └── test_word_editor.py
│   │   └── uv.lock
│   ├── mcp-server-open-deep-research
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server
│   │   │   ├── __init__.py
│   │   │   ├── config.py
│   │   │   ├── libs
│   │   │   │   └── open_deep_research
│   │   │   │       ├── cookies.py
│   │   │   │       ├── mdconvert.py
│   │   │   │       ├── run_agents.py
│   │   │   │       ├── text_inspector_tool.py
│   │   │   │       ├── text_web_browser.py
│   │   │   │       └── visual_qa.py
│   │   │   ├── open_deep_research.py
│   │   │   ├── server.py
│   │   │   └── start.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── mcp-server-open-deep-research-clone
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server_open_deep_research_clone
│   │   │   ├── __init__.py
│   │   │   ├── azure_openai.py
│   │   │   ├── config.py
│   │   │   ├── logging.py
│   │   │   ├── sampling.py
│   │   │   ├── server.py
│   │   │   ├── start.py
│   │   │   ├── utils.py
│   │   │   └── web_research.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── test
│   │   │   └── test_open_deep_research_clone.py
│   │   └── uv.lock
│   ├── mcp-server-template
│   │   ├── .taplo.toml
│   │   ├── .vscode
│   │   │   └── settings.json
│   │   ├── copier.yml
│   │   ├── README.md
│   │   └── template
│   │       └── {{ project_slug }}
│   │           ├── .env.example.jinja
│   │           ├── .gitignore
│   │           ├── .vscode
│   │           │   ├── launch.json.jinja
│   │           │   └── settings.json
│   │           ├── {{ module_name }}
│   │           │   ├── __init__.py
│   │           │   ├── config.py.jinja
│   │           │   ├── server.py.jinja
│   │           │   └── start.py.jinja
│   │           ├── Makefile.jinja
│   │           ├── pyproject.toml.jinja
│   │           └── README.md.jinja
│   ├── mcp-server-vscode
│   │   ├── .eslintrc.cjs
│   │   ├── .gitignore
│   │   ├── .npmrc
│   │   ├── .vscode
│   │   │   ├── extensions.json
│   │   │   ├── launch.json
│   │   │   ├── settings.json
│   │   │   └── tasks.json
│   │   ├── .vscode-test.mjs
│   │   ├── .vscodeignore
│   │   ├── ASSISTANT_BOOTSTRAP.md
│   │   ├── eslint.config.mjs
│   │   ├── images
│   │   │   └── icon.png
│   │   ├── LICENSE
│   │   ├── Makefile
│   │   ├── out
│   │   │   ├── extension.d.ts
│   │   │   ├── extension.js
│   │   │   ├── test
│   │   │   │   ├── extension.test.d.ts
│   │   │   │   └── extension.test.js
│   │   │   ├── tools
│   │   │   │   ├── code_checker.d.ts
│   │   │   │   ├── code_checker.js
│   │   │   │   ├── debug_tools.d.ts
│   │   │   │   ├── debug_tools.js
│   │   │   │   ├── focus_editor.d.ts
│   │   │   │   ├── focus_editor.js
│   │   │   │   ├── search_symbol.d.ts
│   │   │   │   └── search_symbol.js
│   │   │   └── utils
│   │   │       ├── port.d.ts
│   │   │       └── port.js
│   │   ├── package.json
│   │   ├── pnpm-lock.yaml
│   │   ├── prettier.config.cjs
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── extension.d.ts
│   │   │   ├── extension.ts
│   │   │   ├── test
│   │   │   │   ├── extension.test.d.ts
│   │   │   │   └── extension.test.ts
│   │   │   ├── tools
│   │   │   │   ├── code_checker.d.ts
│   │   │   │   ├── code_checker.ts
│   │   │   │   ├── debug_tools.d.ts
│   │   │   │   ├── debug_tools.ts
│   │   │   │   ├── focus_editor.d.ts
│   │   │   │   ├── focus_editor.ts
│   │   │   │   ├── search_symbol.d.ts
│   │   │   │   └── search_symbol.ts
│   │   │   └── utils
│   │   │       ├── port.d.ts
│   │   │       └── port.ts
│   │   ├── tsconfig.json
│   │   ├── tsconfig.tsbuildinfo
│   │   ├── vsc-extension-quickstart.md
│   │   └── webpack.config.js
│   └── mcp-server-web-research
│       ├── .env.example
│       ├── .gitignore
│       ├── .vscode
│       │   ├── launch.json
│       │   └── settings.json
│       ├── Makefile
│       ├── mcp_server_web_research
│       │   ├── __init__.py
│       │   ├── azure_openai.py
│       │   ├── config.py
│       │   ├── logging.py
│       │   ├── sampling.py
│       │   ├── server.py
│       │   ├── start.py
│       │   ├── utils.py
│       │   └── web_research.py
│       ├── pyproject.toml
│       ├── README.md
│       ├── test
│       │   └── test_web_research.py
│       └── uv.lock
├── README.md
├── RESPONSIBLE_AI_FAQ.md
├── ruff.toml
├── SECURITY.md
├── semantic-workbench.code-workspace
├── SUPPORT.md
├── tools
│   ├── build_ai_context_files.py
│   ├── collect_files.py
│   ├── docker
│   │   ├── azure_website_sshd.conf
│   │   ├── docker-entrypoint.sh
│   │   ├── Dockerfile.assistant
│   │   └── Dockerfile.mcp-server
│   ├── makefiles
│   │   ├── docker-assistant.mk
│   │   ├── docker-mcp-server.mk
│   │   ├── docker.mk
│   │   ├── python.mk
│   │   ├── recursive.mk
│   │   └── shell.mk
│   ├── reset-service-data.ps1
│   ├── reset-service-data.sh
│   ├── run-app.ps1
│   ├── run-app.sh
│   ├── run-canonical-agent.ps1
│   ├── run-canonical-agent.sh
│   ├── run-dotnet-examples-with-aspire.sh
│   ├── run-python-example1.sh
│   ├── run-python-example2.ps1
│   ├── run-python-example2.sh
│   ├── run-service.ps1
│   ├── run-service.sh
│   ├── run-workbench-chatbot.ps1
│   └── run-workbench-chatbot.sh
├── workbench-app
│   ├── .dockerignore
│   ├── .env.example
│   ├── .eslintrc.cjs
│   ├── .gitignore
│   ├── .vscode
│   │   ├── launch.json
│   │   └── settings.json
│   ├── docker-entrypoint.sh
│   ├── Dockerfile
│   ├── docs
│   │   ├── APP_DEV_GUIDE.md
│   │   ├── MESSAGE_METADATA.md
│   │   ├── MESSAGE_TYPES.md
│   │   ├── README.md
│   │   └── STATE_INSPECTORS.md
│   ├── index.html
│   ├── Makefile
│   ├── nginx.conf
│   ├── package.json
│   ├── pnpm-lock.yaml
│   ├── prettier.config.cjs
│   ├── public
│   │   └── assets
│   │       ├── background-1-upscaled.jpg
│   │       ├── background-1-upscaled.png
│   │       ├── background-1.jpg
│   │       ├── background-1.png
│   │       ├── background-2.jpg
│   │       ├── background-2.png
│   │       ├── experimental-feature.jpg
│   │       ├── favicon.svg
│   │       ├── workflow-designer-1.jpg
│   │       ├── workflow-designer-outlets.jpg
│   │       ├── workflow-designer-states.jpg
│   │       └── workflow-designer-transitions.jpg
│   ├── README.md
│   ├── run.sh
│   ├── src
│   │   ├── components
│   │   │   ├── App
│   │   │   │   ├── AppFooter.tsx
│   │   │   │   ├── AppHeader.tsx
│   │   │   │   ├── AppMenu.tsx
│   │   │   │   ├── AppView.tsx
│   │   │   │   ├── CodeLabel.tsx
│   │   │   │   ├── CommandButton.tsx
│   │   │   │   ├── ConfirmLeave.tsx
│   │   │   │   ├── ContentExport.tsx
│   │   │   │   ├── ContentImport.tsx
│   │   │   │   ├── CopyButton.tsx
│   │   │   │   ├── DialogControl.tsx
│   │   │   │   ├── DynamicIframe.tsx
│   │   │   │   ├── ErrorListFromAppState.tsx
│   │   │   │   ├── ErrorMessageBar.tsx
│   │   │   │   ├── ExperimentalNotice.tsx
│   │   │   │   ├── FormWidgets
│   │   │   │   │   ├── BaseModelEditorWidget.tsx
│   │   │   │   │   ├── CustomizedArrayFieldTemplate.tsx
│   │   │   │   │   ├── CustomizedFieldTemplate.tsx
│   │   │   │   │   ├── CustomizedObjectFieldTemplate.tsx
│   │   │   │   │   └── InspectableWidget.tsx
│   │   │   │   ├── LabelWithDescription.tsx
│   │   │   │   ├── Loading.tsx
│   │   │   │   ├── MenuItemControl.tsx
│   │   │   │   ├── MiniControl.tsx
│   │   │   │   ├── MyAssistantServiceRegistrations.tsx
│   │   │   │   ├── MyItemsManager.tsx
│   │   │   │   ├── OverflowMenu.tsx
│   │   │   │   ├── PresenceMotionList.tsx
│   │   │   │   ├── ProfileSettings.tsx
│   │   │   │   └── TooltipWrapper.tsx
│   │   │   ├── Assistants
│   │   │   │   ├── ApplyConfigButton.tsx
│   │   │   │   ├── AssistantAdd.tsx
│   │   │   │   ├── AssistantConfigExportButton.tsx
│   │   │   │   ├── AssistantConfigImportButton.tsx
│   │   │   │   ├── AssistantConfiguration.tsx
│   │   │   │   ├── AssistantConfigure.tsx
│   │   │   │   ├── AssistantCreate.tsx
│   │   │   │   ├── AssistantDelete.tsx
│   │   │   │   ├── AssistantDuplicate.tsx
│   │   │   │   ├── AssistantExport.tsx
│   │   │   │   ├── AssistantImport.tsx
│   │   │   │   ├── AssistantRemove.tsx
│   │   │   │   ├── AssistantRename.tsx
│   │   │   │   ├── AssistantServiceInfo.tsx
│   │   │   │   ├── AssistantServiceMetadata.tsx
│   │   │   │   └── MyAssistants.tsx
│   │   │   ├── AssistantServiceRegistrations
│   │   │   │   ├── AssistantServiceRegistrationApiKey.tsx
│   │   │   │   ├── AssistantServiceRegistrationApiKeyReset.tsx
│   │   │   │   ├── AssistantServiceRegistrationCreate.tsx
│   │   │   │   └── AssistantServiceRegistrationRemove.tsx
│   │   │   ├── Conversations
│   │   │   │   ├── Canvas
│   │   │   │   │   ├── AssistantCanvas.tsx
│   │   │   │   │   ├── AssistantCanvasList.tsx
│   │   │   │   │   ├── AssistantInspector.tsx
│   │   │   │   │   ├── AssistantInspectorList.tsx
│   │   │   │   │   └── ConversationCanvas.tsx
│   │   │   │   ├── ChatInputPlugins
│   │   │   │   │   ├── ClearEditorPlugin.tsx
│   │   │   │   │   ├── LexicalMenu.ts
│   │   │   │   │   ├── ParticipantMentionsPlugin.tsx
│   │   │   │   │   ├── TypeaheadMenuPlugin.css
│   │   │   │   │   └── TypeaheadMenuPlugin.tsx
│   │   │   │   ├── ContentRenderers
│   │   │   │   │   ├── CodeContentRenderer.tsx
│   │   │   │   │   ├── ContentListRenderer.tsx
│   │   │   │   │   ├── ContentRenderer.tsx
│   │   │   │   │   ├── DiffRenderer.tsx
│   │   │   │   │   ├── HtmlContentRenderer.tsx
│   │   │   │   │   ├── JsonSchemaContentRenderer.tsx
│   │   │   │   │   ├── MarkdownContentRenderer.tsx
│   │   │   │   │   ├── MarkdownEditorRenderer.tsx
│   │   │   │   │   ├── MermaidContentRenderer.tsx
│   │   │   │   │   ├── MusicABCContentRenderer.css
│   │   │   │   │   └── MusicABCContentRenderer.tsx
│   │   │   │   ├── ContextWindow.tsx
│   │   │   │   ├── ConversationCreate.tsx
│   │   │   │   ├── ConversationDuplicate.tsx
│   │   │   │   ├── ConversationExport.tsx
│   │   │   │   ├── ConversationFileIcon.tsx
│   │   │   │   ├── ConversationRemove.tsx
│   │   │   │   ├── ConversationRename.tsx
│   │   │   │   ├── ConversationShare.tsx
│   │   │   │   ├── ConversationShareCreate.tsx
│   │   │   │   ├── ConversationShareList.tsx
│   │   │   │   ├── ConversationShareView.tsx
│   │   │   │   ├── ConversationsImport.tsx
│   │   │   │   ├── ConversationTranscript.tsx
│   │   │   │   ├── DebugInspector.tsx
│   │   │   │   ├── FileItem.tsx
│   │   │   │   ├── FileList.tsx
│   │   │   │   ├── InputAttachmentList.tsx
│   │   │   │   ├── InputOptionsControl.tsx
│   │   │   │   ├── InteractHistory.tsx
│   │   │   │   ├── InteractInput.tsx
│   │   │   │   ├── Message
│   │   │   │   │   ├── AttachmentSection.tsx
│   │   │   │   │   ├── ContentRenderer.tsx
│   │   │   │   │   ├── ContentSafetyNotice.tsx
│   │   │   │   │   ├── InteractMessage.tsx
│   │   │   │   │   ├── MessageActions.tsx
│   │   │   │   │   ├── MessageBase.tsx
│   │   │   │   │   ├── MessageBody.tsx
│   │   │   │   │   ├── MessageContent.tsx
│   │   │   │   │   ├── MessageFooter.tsx
│   │   │   │   │   ├── MessageHeader.tsx
│   │   │   │   │   ├── NotificationAccordion.tsx
│   │   │   │   │   └── ToolResultMessage.tsx
│   │   │   │   ├── MessageDelete.tsx
│   │   │   │   ├── MessageLink.tsx
│   │   │   │   ├── MyConversations.tsx
│   │   │   │   ├── MyShares.tsx
│   │   │   │   ├── ParticipantAvatar.tsx
│   │   │   │   ├── ParticipantAvatarGroup.tsx
│   │   │   │   ├── ParticipantItem.tsx
│   │   │   │   ├── ParticipantList.tsx
│   │   │   │   ├── ParticipantStatus.tsx
│   │   │   │   ├── RewindConversation.tsx
│   │   │   │   ├── ShareRemove.tsx
│   │   │   │   ├── SpeechButton.tsx
│   │   │   │   └── ToolCalls.tsx
│   │   │   └── FrontDoor
│   │   │       ├── Chat
│   │   │       │   ├── AssistantDrawer.tsx
│   │   │       │   ├── CanvasDrawer.tsx
│   │   │       │   ├── Chat.tsx
│   │   │       │   ├── ChatCanvas.tsx
│   │   │       │   ├── ChatControls.tsx
│   │   │       │   └── ConversationDrawer.tsx
│   │   │       ├── Controls
│   │   │       │   ├── AssistantCard.tsx
│   │   │       │   ├── AssistantSelector.tsx
│   │   │       │   ├── AssistantServiceSelector.tsx
│   │   │       │   ├── ConversationItem.tsx
│   │   │       │   ├── ConversationList.tsx
│   │   │       │   ├── ConversationListOptions.tsx
│   │   │       │   ├── NewConversationButton.tsx
│   │   │       │   ├── NewConversationForm.tsx
│   │   │       │   └── SiteMenuButton.tsx
│   │   │       ├── GlobalContent.tsx
│   │   │       └── MainContent.tsx
│   │   ├── Constants.ts
│   │   ├── global.d.ts
│   │   ├── index.css
│   │   ├── libs
│   │   │   ├── AppStorage.ts
│   │   │   ├── AuthHelper.ts
│   │   │   ├── EventSubscriptionManager.ts
│   │   │   ├── Theme.ts
│   │   │   ├── useAssistantCapabilities.ts
│   │   │   ├── useChatCanvasController.ts
│   │   │   ├── useConversationEvents.ts
│   │   │   ├── useConversationUtility.ts
│   │   │   ├── useCreateConversation.ts
│   │   │   ├── useDebugComponentLifecycle.ts
│   │   │   ├── useDragAndDrop.ts
│   │   │   ├── useEnvironment.ts
│   │   │   ├── useExportUtility.ts
│   │   │   ├── useHistoryUtility.ts
│   │   │   ├── useKeySequence.ts
│   │   │   ├── useMediaQuery.ts
│   │   │   ├── useMicrosoftGraph.ts
│   │   │   ├── useNotify.tsx
│   │   │   ├── useParticipantUtility.tsx
│   │   │   ├── useSiteUtility.ts
│   │   │   ├── useWorkbenchEventSource.ts
│   │   │   ├── useWorkbenchService.ts
│   │   │   └── Utility.ts
│   │   ├── main.tsx
│   │   ├── models
│   │   │   ├── Assistant.ts
│   │   │   ├── AssistantCapability.ts
│   │   │   ├── AssistantServiceInfo.ts
│   │   │   ├── AssistantServiceRegistration.ts
│   │   │   ├── Config.ts
│   │   │   ├── Conversation.ts
│   │   │   ├── ConversationFile.ts
│   │   │   ├── ConversationMessage.ts
│   │   │   ├── ConversationMessageDebug.ts
│   │   │   ├── ConversationParticipant.ts
│   │   │   ├── ConversationShare.ts
│   │   │   ├── ConversationShareRedemption.ts
│   │   │   ├── ConversationState.ts
│   │   │   ├── ConversationStateDescription.ts
│   │   │   ├── ServiceEnvironment.ts
│   │   │   └── User.ts
│   │   ├── redux
│   │   │   ├── app
│   │   │   │   ├── hooks.ts
│   │   │   │   ├── rtkQueryErrorLogger.ts
│   │   │   │   └── store.ts
│   │   │   └── features
│   │   │       ├── app
│   │   │       │   ├── appSlice.ts
│   │   │       │   └── AppState.ts
│   │   │       ├── chatCanvas
│   │   │       │   ├── chatCanvasSlice.ts
│   │   │       │   └── ChatCanvasState.ts
│   │   │       ├── localUser
│   │   │       │   ├── localUserSlice.ts
│   │   │       │   └── LocalUserState.ts
│   │   │       └── settings
│   │   │           ├── settingsSlice.ts
│   │   │           └── SettingsState.ts
│   │   ├── Root.tsx
│   │   ├── routes
│   │   │   ├── AcceptTerms.tsx
│   │   │   ├── AssistantEditor.tsx
│   │   │   ├── AssistantServiceRegistrationEditor.tsx
│   │   │   ├── Dashboard.tsx
│   │   │   ├── ErrorPage.tsx
│   │   │   ├── FrontDoor.tsx
│   │   │   ├── Login.tsx
│   │   │   ├── Settings.tsx
│   │   │   ├── ShareRedeem.tsx
│   │   │   └── Shares.tsx
│   │   ├── services
│   │   │   └── workbench
│   │   │       ├── assistant.ts
│   │   │       ├── assistantService.ts
│   │   │       ├── conversation.ts
│   │   │       ├── file.ts
│   │   │       ├── index.ts
│   │   │       ├── participant.ts
│   │   │       ├── share.ts
│   │   │       ├── state.ts
│   │   │       └── workbench.ts
│   │   └── vite-env.d.ts
│   ├── tools
│   │   └── filtered-ts-prune.cjs
│   ├── tsconfig.json
│   └── vite.config.ts
└── workbench-service
    ├── .env.example
    ├── .vscode
    │   ├── extensions.json
    │   ├── launch.json
    │   └── settings.json
    ├── alembic.ini
    ├── devdb
    │   ├── docker-compose.yaml
    │   └── postgresql-init.sh
    ├── Dockerfile
    ├── Makefile
    ├── migrations
    │   ├── env.py
    │   ├── README
    │   ├── script.py.mako
    │   └── versions
    │       ├── 2024_09_19_000000_69dcda481c14_init.py
    │       ├── 2024_09_19_190029_dffb1d7e219a_file_version_filename.py
    │       ├── 2024_09_20_204130_b29524775484_share.py
    │       ├── 2024_10_30_231536_039bec8edc33_index_message_type.py
    │       ├── 2024_11_04_204029_5149c7fb5a32_conversationmessagedebug.py
    │       ├── 2024_11_05_015124_245baf258e11_double_check_debugs.py
    │       ├── 2024_11_25_191056_a106de176394_drop_workflow.py
    │       ├── 2025_03_19_140136_aaaf792d4d72_set_user_title_set.py
    │       ├── 2025_03_21_153250_3763629295ad_add_assistant_template_id.py
    │       ├── 2025_05_19_163613_b2f86e981885_delete_context_transfer_assistants.py
    │       └── 2025_06_18_174328_503c739152f3_delete_knowlege_transfer_assistants.py
    ├── pyproject.toml
    ├── README.md
    ├── semantic_workbench_service
    │   ├── __init__.py
    │   ├── api.py
    │   ├── assistant_api_key.py
    │   ├── auth.py
    │   ├── azure_speech.py
    │   ├── config.py
    │   ├── controller
    │   │   ├── __init__.py
    │   │   ├── assistant_service_client_pool.py
    │   │   ├── assistant_service_registration.py
    │   │   ├── assistant.py
    │   │   ├── conversation_share.py
    │   │   ├── conversation.py
    │   │   ├── convert.py
    │   │   ├── exceptions.py
    │   │   ├── export_import.py
    │   │   ├── file.py
    │   │   ├── participant.py
    │   │   └── user.py
    │   ├── db.py
    │   ├── event.py
    │   ├── files.py
    │   ├── logging_config.py
    │   ├── middleware.py
    │   ├── query.py
    │   ├── service_user_principals.py
    │   ├── service.py
    │   └── start.py
    ├── tests
    │   ├── __init__.py
    │   ├── conftest.py
    │   ├── docker-compose.yaml
    │   ├── test_assistant_api_key.py
    │   ├── test_files.py
    │   ├── test_integration.py
    │   ├── test_middleware.py
    │   ├── test_migrations.py
    │   ├── test_workbench_service.py
    │   └── types.py
    └── uv.lock
```

# Files

--------------------------------------------------------------------------------
/workbench-service/semantic_workbench_service/controller/assistant_service_registration.py:
--------------------------------------------------------------------------------

```python
  1 | import asyncio
  2 | import datetime
  3 | import logging
  4 | from typing import AsyncContextManager, Awaitable, Callable, Iterable
  5 | 
  6 | from semantic_workbench_api_model.assistant_model import ServiceInfoModel
  7 | from semantic_workbench_api_model.assistant_service_client import AssistantError
  8 | from semantic_workbench_api_model.workbench_model import (
  9 |     AssistantServiceInfoList,
 10 |     AssistantServiceRegistration,
 11 |     AssistantServiceRegistrationList,
 12 |     ConversationEventType,
 13 |     NewAssistantServiceRegistration,
 14 |     UpdateAssistantServiceRegistration,
 15 |     UpdateAssistantServiceRegistrationUrl,
 16 | )
 17 | from sqlalchemy import update
 18 | from sqlmodel import col, or_, select
 19 | from sqlmodel.ext.asyncio.session import AsyncSession
 20 | 
 21 | from .. import assistant_api_key, auth, db, settings
 22 | from ..event import ConversationEventQueueItem
 23 | from . import convert, exceptions
 24 | from . import participant as participant_
 25 | from . import user as user_
 26 | from .assistant_service_client_pool import AssistantServiceClientPool
 27 | 
 28 | logger = logging.getLogger(__name__)
 29 | 
 30 | 
 31 | class AssistantServiceRegistrationController:
 32 |     def __init__(
 33 |         self,
 34 |         get_session: Callable[[], AsyncContextManager[AsyncSession]],
 35 |         notify_event: Callable[[ConversationEventQueueItem], Awaitable],
 36 |         api_key_store: assistant_api_key.ApiKeyStore,
 37 |         client_pool: AssistantServiceClientPool,
 38 |     ) -> None:
 39 |         self._get_session = get_session
 40 |         self._notify_event = notify_event
 41 |         self._api_key_store = api_key_store
 42 |         self._client_pool = client_pool
 43 | 
 44 |     @property
 45 |     def _registration_is_secured(self) -> bool:
 46 |         return settings.service.assistant_api_key.is_secured
 47 | 
 48 |     async def api_key_source(self, assistant_service_id: str) -> str | None:
 49 |         generated_key_name = self._api_key_store.generate_key_name(assistant_service_id)
 50 |         if assistant_service_id == generated_key_name:
 51 |             return await self._api_key_store.get(generated_key_name)
 52 | 
 53 |         async with self._get_session() as session:
 54 |             api_key_name = (
 55 |                 await session.exec(
 56 |                     select(db.AssistantServiceRegistration.api_key_name).where(
 57 |                         db.AssistantServiceRegistration.assistant_service_id == assistant_service_id
 58 |                     )
 59 |                 )
 60 |             ).first()
 61 |         if api_key_name is None:
 62 |             return None
 63 |         return await self._api_key_store.get(api_key_name)
 64 | 
 65 |     async def create_registration(
 66 |         self,
 67 |         user_principal: auth.UserPrincipal,
 68 |         new_assistant_service: NewAssistantServiceRegistration,
 69 |     ) -> AssistantServiceRegistration:
 70 |         async with self._get_session() as session:
 71 |             await user_.add_or_update_user_from(session=session, user_principal=user_principal)
 72 | 
 73 |             assistant_service_id = new_assistant_service.assistant_service_id.strip().lower()
 74 | 
 75 |             existing_registration = (
 76 |                 await session.exec(
 77 |                     select(db.AssistantServiceRegistration).where(
 78 |                         db.AssistantServiceRegistration.assistant_service_id == assistant_service_id
 79 |                     )
 80 |                 )
 81 |             ).first()
 82 |             if existing_registration is not None:
 83 |                 raise exceptions.ConflictError("assistant service with this assistant_service_id already exists")
 84 | 
 85 |             api_key_name = self._api_key_store.generate_key_name(assistant_service_id)
 86 |             registration = db.AssistantServiceRegistration(
 87 |                 assistant_service_id=assistant_service_id,
 88 |                 created_by_user_id=user_principal.user_id,
 89 |                 name=new_assistant_service.name,
 90 |                 description=new_assistant_service.description,
 91 |                 include_in_listing=new_assistant_service.include_in_listing,
 92 |                 api_key_name=api_key_name,
 93 |             )
 94 |             session.add(registration)
 95 |             await session.flush()
 96 |             await session.refresh(registration)
 97 | 
 98 |             api_key = await self._api_key_store.reset(registration.api_key_name)
 99 | 
100 |             await session.commit()
101 | 
102 |         return convert.assistant_service_registration_from_db(
103 |             registration, api_key=api_key, include_api_key_name=self._registration_is_secured
104 |         )
105 | 
106 |     async def get_registrations(
107 |         self, user_ids: set[str], assistant_service_online: bool | None = None
108 |     ) -> AssistantServiceRegistrationList:
109 |         async with self._get_session() as session:
110 |             query_registrations = (
111 |                 select(db.AssistantServiceRegistration)
112 |                 .where(col(db.AssistantServiceRegistration.include_in_listing).is_(True))
113 |                 .order_by(col(db.AssistantServiceRegistration.created_datetime).asc())
114 |             )
115 | 
116 |             if user_ids:
117 |                 query_registrations = select(db.AssistantServiceRegistration).where(
118 |                     col(db.AssistantServiceRegistration.created_by_user_id).in_(user_ids)
119 |                 )
120 | 
121 |             if assistant_service_online is not None:
122 |                 query_registrations = query_registrations.where(
123 |                     col(db.AssistantServiceRegistration.assistant_service_online).is_(True)
124 |                 )
125 | 
126 |             assistant_services = await session.exec(query_registrations)
127 | 
128 |             return convert.assistant_service_registration_list_from_db(
129 |                 assistant_services, include_api_key_name=self._registration_is_secured
130 |             )
131 | 
132 |     async def get_registration(self, assistant_service_id: str) -> AssistantServiceRegistration:
133 |         async with self._get_session() as session:
134 |             registration = (
135 |                 await session.exec(
136 |                     select(db.AssistantServiceRegistration).where(
137 |                         db.AssistantServiceRegistration.assistant_service_id == assistant_service_id
138 |                     )
139 |                 )
140 |             ).first()
141 |             if registration is None:
142 |                 raise exceptions.NotFoundError()
143 | 
144 |             api_key = await self._api_key_store.get(registration.api_key_name)
145 |             masked_api_key = self.mask_api_key(api_key)
146 | 
147 |             return convert.assistant_service_registration_from_db(
148 |                 registration, api_key=masked_api_key, include_api_key_name=self._registration_is_secured
149 |             )
150 | 
151 |     @staticmethod
152 |     def mask_api_key(api_key: str | None) -> str | None:
153 |         if api_key is None:
154 |             return None
155 | 
156 |         unmasked_length = 4
157 |         if len(api_key) <= unmasked_length:
158 |             # return a fixed mask if the api key is too short
159 |             return "*" * 32
160 | 
161 |         # returns partially masked api key
162 |         return f"{api_key[:unmasked_length]}{'*' * (len(api_key) - unmasked_length)}"
163 | 
164 |     async def update_registration(
165 |         self,
166 |         user_principal: auth.UserPrincipal,
167 |         assistant_service_id: str,
168 |         update_assistant_service: UpdateAssistantServiceRegistration,
169 |     ) -> AssistantServiceRegistration:
170 |         async with self._get_session() as session:
171 |             registration_query = (
172 |                 select(db.AssistantServiceRegistration)
173 |                 .where(db.AssistantServiceRegistration.assistant_service_id == assistant_service_id)
174 |                 .with_for_update()
175 |             )
176 |             if self._registration_is_secured:
177 |                 registration_query = registration_query.where(
178 |                     db.AssistantServiceRegistration.created_by_user_id == user_principal.user_id
179 |                 )
180 |             registration = (await session.exec(registration_query)).first()
181 | 
182 |             if registration is None:
183 |                 raise exceptions.NotFoundError()
184 | 
185 |             if "name" in update_assistant_service.model_fields_set:
186 |                 if update_assistant_service.name is None:
187 |                     raise exceptions.InvalidArgumentError("name cannot be null")
188 |                 registration.name = update_assistant_service.name
189 |             if "description" in update_assistant_service.model_fields_set:
190 |                 if update_assistant_service.description is None:
191 |                     raise exceptions.InvalidArgumentError("description cannot be null")
192 |                 registration.description = update_assistant_service.description
193 |             if "include_in_listing" in update_assistant_service.model_fields_set:
194 |                 if update_assistant_service.include_in_listing is None:
195 |                     raise exceptions.InvalidArgumentError("include_in_listing cannot be null")
196 |                 registration.include_in_listing = update_assistant_service.include_in_listing
197 | 
198 |             session.add(registration)
199 |             await session.commit()
200 |             await session.refresh(registration)
201 | 
202 |         return convert.assistant_service_registration_from_db(
203 |             registration, include_api_key_name=self._registration_is_secured
204 |         )
205 | 
206 |     async def update_assistant_service_url(
207 |         self,
208 |         assistant_service_principal: auth.AssistantServicePrincipal,
209 |         assistant_service_id: str,
210 |         update_assistant_service_url: UpdateAssistantServiceRegistrationUrl,
211 |     ) -> tuple[AssistantServiceRegistration, Iterable]:
212 |         if assistant_service_id != assistant_service_principal.assistant_service_id:
213 |             raise exceptions.ForbiddenError()
214 | 
215 |         background_task_args: Iterable = ()
216 |         async with self._get_session() as session:
217 |             registration = (
218 |                 await session.exec(
219 |                     select(db.AssistantServiceRegistration)
220 |                     .where(db.AssistantServiceRegistration.assistant_service_id == assistant_service_id)
221 |                     .with_for_update()
222 |                 )
223 |             ).first()
224 | 
225 |             if registration is None:
226 |                 if self._registration_is_secured:
227 |                     raise exceptions.NotFoundError()
228 | 
229 |                 api_key_name = self._api_key_store.generate_key_name(assistant_service_id.lower())
230 |                 registration = db.AssistantServiceRegistration(
231 |                     assistant_service_id=assistant_service_id,
232 |                     created_by_user_id="semantic-workbench",
233 |                     name=update_assistant_service_url.name,
234 |                     description=update_assistant_service_url.description,
235 |                     include_in_listing=True,
236 |                     api_key_name=api_key_name,
237 |                 )
238 | 
239 |             if self._registration_is_secured and update_assistant_service_url.url.scheme != "https":
240 |                 raise exceptions.InvalidArgumentError("url must be https")
241 | 
242 |             if registration.assistant_service_url != str(update_assistant_service_url.url):
243 |                 registration.assistant_service_url = str(update_assistant_service_url.url)
244 |                 logger.info(
245 |                     "updated assistant service url; assistant_service_id: %s, url: %s",
246 |                     assistant_service_id,
247 |                     registration.assistant_service_url,
248 |                 )
249 | 
250 |             registration.assistant_service_online_expiration_datetime = datetime.datetime.now(
251 |                 datetime.UTC
252 |             ) + datetime.timedelta(seconds=update_assistant_service_url.online_expires_in_seconds)
253 | 
254 |             if not registration.assistant_service_online:
255 |                 registration.assistant_service_online = True
256 |                 background_task_args = (self._update_participants, assistant_service_id)
257 | 
258 |             session.add(registration)
259 |             await session.commit()
260 |             await session.refresh(registration)
261 | 
262 |         return convert.assistant_service_registration_from_db(
263 |             registration, include_api_key_name=self._registration_is_secured
264 |         ), background_task_args
265 | 
266 |     async def _update_participants(
267 |         self,
268 |         assistant_service_id: str,
269 |     ) -> None:
270 |         async with self._get_session() as session:
271 |             participants_and_assistants = await session.exec(
272 |                 select(db.AssistantParticipant, db.Assistant)
273 |                 .join(db.Assistant, col(db.Assistant.assistant_id) == col(db.AssistantParticipant.assistant_id))
274 |                 .where(db.Assistant.assistant_service_id == assistant_service_id)
275 |             )
276 | 
277 |             for participant, assistant in participants_and_assistants:
278 |                 participants = await participant_.get_conversation_participants(
279 |                     session=session, conversation_id=participant.conversation_id, include_inactive=True
280 |                 )
281 |                 await self._notify_event(
282 |                     ConversationEventQueueItem(
283 |                         event=participant_.participant_event(
284 |                             event_type=ConversationEventType.participant_updated,
285 |                             conversation_id=participant.conversation_id,
286 |                             participant=convert.conversation_participant_from_db_assistant(
287 |                                 participant, assistant=assistant
288 |                             ),
289 |                             participants=participants,
290 |                         ),
291 |                         # assistants do not need to receive assistant-participant online/offline events
292 |                         event_audience={"user"},
293 |                     )
294 |                 )
295 | 
296 |     async def reset_api_key(
297 |         self,
298 |         user_principal: auth.UserPrincipal,
299 |         assistant_service_id: str,
300 |     ) -> AssistantServiceRegistration:
301 |         async with self._get_session() as session:
302 |             registration_query = select(db.AssistantServiceRegistration).where(
303 |                 db.AssistantServiceRegistration.assistant_service_id == assistant_service_id
304 |             )
305 |             if self._registration_is_secured:
306 |                 registration_query = registration_query.where(
307 |                     db.AssistantServiceRegistration.created_by_user_id == user_principal.user_id
308 |                 )
309 | 
310 |             registration = (await session.exec(registration_query)).first()
311 |             if registration is None:
312 |                 raise exceptions.NotFoundError()
313 | 
314 |             api_key = await self._api_key_store.reset(registration.api_key_name)
315 | 
316 |         return convert.assistant_service_registration_from_db(
317 |             registration, api_key=api_key, include_api_key_name=self._registration_is_secured
318 |         )
319 | 
320 |     async def check_assistant_service_online_expired(self) -> None:
321 |         async with self._get_session() as session:
322 |             conn = await session.connection()
323 |             result = await conn.execute(
324 |                 update(db.AssistantServiceRegistration)
325 |                 .where(col(db.AssistantServiceRegistration.assistant_service_online).is_(True))
326 |                 .where(
327 |                     or_(
328 |                         col(db.AssistantServiceRegistration.assistant_service_online_expiration_datetime).is_(None),
329 |                         col(db.AssistantServiceRegistration.assistant_service_online_expiration_datetime)
330 |                         <= datetime.datetime.now(
331 |                             datetime.UTC,
332 |                         ),
333 |                     ),
334 |                 )
335 |                 .values(assistant_service_online=False)
336 |                 .returning(col(db.AssistantServiceRegistration.assistant_service_id))
337 |             )
338 |             if not result.rowcount:
339 |                 return
340 | 
341 |             assistant_service_ids = result.scalars().all()
342 |             await session.commit()
343 | 
344 |         for assistant_service_id in assistant_service_ids:
345 |             await self._update_participants(assistant_service_id=assistant_service_id)
346 | 
347 |     async def delete_registration(
348 |         self,
349 |         user_principal: auth.UserPrincipal,
350 |         assistant_service_id: str,
351 |     ) -> None:
352 |         async with self._get_session() as session:
353 |             registration = (
354 |                 await session.exec(
355 |                     select(db.AssistantServiceRegistration)
356 |                     .where(db.AssistantServiceRegistration.assistant_service_id == assistant_service_id)
357 |                     .where(db.AssistantServiceRegistration.created_by_user_id == user_principal.user_id)
358 |                 )
359 |             ).first()
360 |             if registration is None:
361 |                 raise exceptions.NotFoundError()
362 | 
363 |             await session.delete(registration)
364 |             await session.commit()
365 | 
366 |             await self._api_key_store.delete(registration.api_key_name)
367 | 
368 |     async def get_service_info(self, assistant_service_id: str) -> ServiceInfoModel:
369 |         async with self._get_session() as session:
370 |             registration = (
371 |                 await session.exec(
372 |                     select(db.AssistantServiceRegistration).where(
373 |                         db.AssistantServiceRegistration.assistant_service_id == assistant_service_id
374 |                     )
375 |                 )
376 |             ).first()
377 | 
378 |             if registration is None:
379 |                 raise exceptions.NotFoundError()
380 | 
381 |             if not registration.assistant_service_online:
382 |                 raise exceptions.NotFoundError()
383 | 
384 |         return await (await self._client_pool.service_client(registration=registration)).get_service_info()
385 | 
386 |     async def get_service_infos(self, user_ids: set[str] = set()) -> AssistantServiceInfoList:
387 |         async with self._get_session() as session:
388 |             query_registrations = (
389 |                 select(db.AssistantServiceRegistration)
390 |                 .where(col(db.AssistantServiceRegistration.include_in_listing).is_(True))
391 |                 .order_by(col(db.AssistantServiceRegistration.created_datetime).asc())
392 |             )
393 | 
394 |             if user_ids:
395 |                 query_registrations = select(db.AssistantServiceRegistration).where(
396 |                     col(db.AssistantServiceRegistration.created_by_user_id).in_(user_ids)
397 |                 )
398 | 
399 |             query_registrations = query_registrations.where(
400 |                 col(db.AssistantServiceRegistration.assistant_service_online).is_(True)
401 |             )
402 | 
403 |             assistant_services = await session.exec(query_registrations)
404 | 
405 |         infos_or_exceptions = await asyncio.gather(
406 |             *[
407 |                 (await self._client_pool.service_client(registration=registration)).get_service_info()
408 |                 for registration in assistant_services
409 |             ],
410 |             return_exceptions=True,
411 |         )
412 | 
413 |         infos: list[ServiceInfoModel] = []
414 |         for info_or_exception in infos_or_exceptions:
415 |             match info_or_exception:
416 |                 case AssistantError():
417 |                     logger.error("failed to get assistant service info", exc_info=info_or_exception)
418 | 
419 |                 case BaseException():
420 |                     raise info_or_exception
421 | 
422 |                 case ServiceInfoModel():
423 |                     infos.append(info_or_exception)
424 | 
425 |         return AssistantServiceInfoList(assistant_service_infos=infos)
426 | 
```

--------------------------------------------------------------------------------
/libraries/python/skills/skill-library/skill_library/skills/guided_conversation/chat_completions/generate_agenda.py:
--------------------------------------------------------------------------------

```python
  1 | """
  2 | `update_agenda` will run a chat completion to create an agenda for the
  3 | conversation. The completion will be based on the current state of the
  4 | conversation, the artifact, and the resource constraints. The completion will
  5 | provide a list of items to be completed sequentially, where the first item
  6 | contains everything that will be done in the current turn of the conversation.
  7 | The completion will also provide an estimate of the number of turns required to
  8 | complete each item. The completion will ensure that the total number of turns
  9 | allocated across all items in the updated agenda does not exceed the remaining
 10 | turns available. If the completion fails, the function will attempt to fix the
 11 | error and generate a new agenda. The function will return the updated agenda and
 12 | a boolean indicating whether the conversation is complete. If the completion
 13 | fails after multiple attempts, the function will return the current agenda and a
 14 | boolean indicating that the conversation is not complete. The function will log
 15 | any errors that occur during the completion process.
 16 | 
 17 | How do agendas work? See:
 18 | https://microsoft.sharepoint.com/:v:/t/NERDAIProgram2/EfRcEA2RSP9DuJhw8AHnAP4B12g__TFV21GOxlZvSR3mEA?e=91Wp9f&nav=eyJwbGF5YmFja09wdGlvbnMiOnt9LCJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJTaGFyZVBvaW50IiwicmVmZXJyYWxNb2RlIjoibWlzIiwicmVmZXJyYWxWaWV3IjoidmlkZW9hY3Rpb25zLXNoYXJlIiwicmVmZXJyYWxQbGF5YmFja1Nlc3Npb25JZCI6ImMzYzUwNTEwLWQ1MzAtNGQyYS1iZGY3LTE2ZGViZTYwNjU4YiJ9fQ%3D%3D
 19 | """
 20 | 
 21 | from typing import Any, cast
 22 | 
 23 | from openai_client import (
 24 |     CompletionError,
 25 |     create_system_message,
 26 |     create_user_message,
 27 |     make_completion_args_serializable,
 28 |     validate_completion,
 29 | )
 30 | from pydantic import ValidationError
 31 | from skill_library.skills.guided_conversation.agenda import Agenda, AgendaItem
 32 | from skill_library.skills.guided_conversation.artifact_helpers import get_artifact_for_prompt
 33 | from skill_library.skills.guided_conversation.guide import ConversationGuide
 34 | from skill_library.skills.guided_conversation.message import Conversation
 35 | from skill_library.skills.guided_conversation.resources import (
 36 |     ConversationResource,
 37 |     ResourceConstraintMode,
 38 |     ResourceConstraintUnit,
 39 | )
 40 | from skill_library.types import LanguageModel
 41 | 
 42 | from ..logging import extra_data, logger
 43 | from .fix_agenda_error import fix_agenda_error
 44 | 
 45 | GENERATE_AGENDA_TEMPLATE = """
 46 | You are a helpful, thoughtful, and meticulous assistant. You are conducting a conversation with a user. Your goal is to complete an artifact as thoroughly as possible by the end of the conversation, and to ensure a smooth experience for the user.
 47 | 
 48 | This is the schema of the artifact you are completing:
 49 | {{ artifact_schema }}
 50 | 
 51 | {% if context %}
 52 | Here is some additional context about the conversation:
 53 | {{ context }}
 54 | {% endif %}
 55 | 
 56 | Throughout the conversation, you must abide by these rules:
 57 | {{ rules }}
 58 | 
 59 | {% if current_state_description %}
 60 | Here's a description of the conversation flow:
 61 | {{ current_state_description }}
 62 | 
 63 | Follow this description, and exercise good judgment about when it is appropriate to deviate.
 64 | {% endif %}
 65 | 
 66 | You will be provided the history of your conversation with the user up until now and the current state of the artifact. Note that if a required field from the artifact schema is missing from the artifact, it means that the field has not been completed. You need to create an agenda for the remaining conversation given the state of the conversation and the artifact.
 67 | 
 68 | How to update the agenda:
 69 | 
 70 | - If you need to change your plan for the conversation to make the best use of the remaining turns available to you. Consider how long it usually takes to get the information you need (which is a function of the quality and pace of the user's responses), the number, complexity, and importance of the remaining fields in the artifact, and the number of turns remaining ({{ remaining_resource }}). Based on these factors, you might need to accelerate (e.g. combine several topics) or slow down the conversation (e.g. spread out a topic), in which case you should update the agenda accordingly. Note that skipping an artifact field is NOT a valid way to accelerate the conversation.
 71 | - If you do not need to change your plan, just return the list of agenda items as is.
 72 | - You must provide an ordered list of items to be completed sequentially, where the first item contains everything you will do in the current turn of the conversation (in addition to updating the agenda). For example, if you choose to send a message to the user asking for their name and medical history, then you would write "ask for name and medical history" as the first item. If you think medical history will take longer than asking for the name, then you would write "complete medical history" as the second item, with an estimate of how many turns you think it will take. Do NOT include items that have already been completed. Items must always represent a conversation topic (corresponding to the "Send message to user" action). Updating the artifact (e.g. "update field X based on the discussion") or terminating the conversation is NOT a valid item.
 73 | - The latest agenda was created in the previous turn of the conversation. Even if the total turns in the latest agenda equals the remaining turns, you should still update the agenda if you think the current plan is suboptimal (e.g. the first item was completed, the order of items is not ideal, an item is too broad or not a conversation topic, etc.).
 74 | - Each item must have a description and and your best guess for the number of turns required to complete it. Do not provide a range of turns. It is EXTREMELY important that the total turns allocated across all items in the updated agenda (including the first item for the current turn) {{ total_resource_phrase }} Everything in the agenda should be something you expect to complete in the remaining turns - there shouldn't be any optional "buffer" items. It can be helpful to include the cumulative turns allocated for each item in the agenda to ensure you adhere to this rule, e.g. item 1 = 2 turns (cumulative total = 2), item 2 = 4 turns (cumulative total = 6), etc.
 75 | - Avoid high-level items like "ask follow-up questions" - be specific about what you need to do.
 76 | - Do NOT include wrap-up items such as "review and confirm all information with the user" (you should be doing this throughout the conversation) or "thank the user for their time". Do NOT repeat topics that have already been sufficiently addressed. {{ ample_time_phrase }}
 77 | 
 78 | When you determine the conversation is completed, just return an agenda with no items in it.
 79 | """.replace("\n\n\n", "\n\n").strip()
 80 | 
 81 | 
 82 | def resource_phrase(quantity: float, unit: ResourceConstraintUnit) -> str:
 83 |     """
 84 |     Get rounded, formatted string for a given quantity and unit (e.g. 1
 85 |     turn/second/minute, 20 turns/seconds/minutes).
 86 |     """
 87 |     quantity = round(quantity)
 88 |     s = f"{quantity} {unit.value}"
 89 | 
 90 |     # Remove the 's' from if the quantity is 1.
 91 |     if quantity == 1:
 92 |         return s[:-1]
 93 |     else:
 94 |         return s
 95 | 
 96 | 
 97 | def resource_instructions(resource: ConversationResource) -> str:
 98 |     """
 99 |     Get the resource instructions for the conversation.
100 | 
101 |     Note: Assumes we're always using turns as the resource unit.
102 | 
103 |     Returns:
104 |         str: the resource instructions
105 |     """
106 |     if resource.resource_constraint is None:
107 |         return ""
108 | 
109 |     is_plural_elapsed = resource.elapsed_units != 1
110 |     is_plural_remaining = resource.remaining_units != 1
111 | 
112 |     if resource.elapsed_units > 0:
113 |         elapsed_resource_phrase = resource_phrase(resource.elapsed_units, ResourceConstraintUnit.TURNS)
114 |         instructions = (
115 |             f"So far, {elapsed_resource_phrase} {'have' if is_plural_elapsed else 'has'} "
116 |             "elapsed since the conversation began. "
117 |         )
118 |     else:
119 |         instructions = ""
120 | 
121 |     remaining_resource_phrase = resource_phrase(resource.remaining_units, ResourceConstraintUnit.TURNS)
122 |     if resource.resource_constraint.mode == ResourceConstraintMode.EXACT:
123 |         exact_mode_instructions = (
124 |             f"There {'are' if is_plural_remaining else 'is'} {remaining_resource_phrase} "
125 |             "remaining (including this one) - the conversation will automatically terminate "
126 |             "when 0 turns are left. You should continue the conversation until it is "
127 |             "automatically terminated. This means you should NOT preemptively end the "
128 |             'conversation, either explicitly (by selecting the "End conversation" action) '
129 |             "or implicitly (e.g. by telling the user that you have all required information "
130 |             "and they should wait for the next step). Your goal is not to maximize efficiency "
131 |             "(i.e. complete the artifact as quickly as possible then end the conversation), "
132 |             "but rather to make the best use of ALL remaining turns available to you"
133 |         )
134 | 
135 |         if is_plural_remaining:
136 |             instructions += (
137 |                 f"{exact_mode_instructions}. This will require you to "
138 |                 "plan your actions carefully using the agenda: you want to avoid the situation "
139 |                 "where you have to pack too many topics into the final turns because you didn't "
140 |                 "account for them earlier, or where you've rushed through the conversation and "
141 |                 "all fields are completed but there are still many turns left."
142 |             )
143 | 
144 |         # Special instruction for the final turn (i.e. 1 remaining) in exact mode.
145 |         else:
146 |             instructions += (
147 |                 f"{exact_mode_instructions}, including this one. Therefore, you should use this "
148 |                 "turn to ask for any remaining information needed to complete the artifact, or, "
149 |                 "if the artifact is already completed, continue to broaden/deepen the discussion "
150 |                 "in a way that's directly relevant to the artifact. Do NOT indicate to the user "
151 |                 "that the conversation is ending."
152 |             )
153 | 
154 |     elif resource.resource_constraint.mode == ResourceConstraintMode.MAXIMUM:
155 |         instructions += (
156 |             f"You have a maximum of {remaining_resource_phrase} (including this one) left to "
157 |             "complete the conversation. You can decide to terminate the conversation at any point "
158 |             "(including now), otherwise the conversation will automatically terminate when 0 turns "
159 |             "are left. You will need to plan your actions carefully using the agenda: you want to "
160 |             "avoid the situation where you have to pack too many topics into the final turns because "
161 |             "you didn't account for them earlier."
162 |         )
163 |     else:
164 |         logger.error("Invalid resource mode provided.")
165 | 
166 |     return instructions
167 | 
168 | 
169 | def agenda_phrase(agenda: Agenda) -> str:
170 |     """
171 |     Gets a string representation of the agenda for use in an LLM prompt.
172 |     """
173 |     if not agenda.items:
174 |         return "None"
175 |     item_list = "\n".join([
176 |         f"{i + 1}. [{resource_phrase(item.resource, ResourceConstraintUnit.TURNS)}] {item.title}"
177 |         for i, item in enumerate(agenda.items)
178 |     ])
179 |     total_resource = resource_phrase(sum([item.resource for item in agenda.items]), ResourceConstraintUnit.TURNS)
180 |     return item_list + f"\nTotal = {total_resource}"
181 | 
182 | 
183 | def check_item_constraints(
184 |     resource_constraint_mode: ResourceConstraintMode,
185 |     items: list[AgendaItem],
186 |     remaining_turns: int,
187 | ) -> None:
188 |     """
189 |     Validates if any constraints were violated while performing the agenda
190 |     update.
191 |     """
192 |     # The total, proposed allocation of resources.
193 |     total_resources = sum([item.resource for item in items])
194 | 
195 |     violations = []
196 |     # In maximum mode, the total resources should not exceed the remaining
197 |     # turns.
198 |     if (resource_constraint_mode == ResourceConstraintMode.MAXIMUM) and (total_resources > remaining_turns):
199 |         violations.append(
200 |             "The total turns allocated in the agenda "
201 |             f"must not exceed the remaining amount ({remaining_turns}); "
202 |             f"but the current total is {total_resources}."
203 |         )
204 | 
205 |     # In exact mode if the total resources were not exactly equal to the
206 |     # remaining turns.
207 |     if (resource_constraint_mode == ResourceConstraintMode.EXACT) and (total_resources != remaining_turns):
208 |         violations.append(
209 |             "The total turns allocated in the agenda "
210 |             f"must equal the remaining amount ({remaining_turns}); "
211 |             f"but the current total is {total_resources}."
212 |         )
213 | 
214 |     # Check if any item has a resource value of 0.
215 |     if any(item.resource <= 0 for item in items):
216 |         violations.append("All items must have a resource value greater than 0.")
217 | 
218 |     # Raise an error if any violations were found.
219 |     if len(violations) > 0:
220 |         logger.debug(f"Agenda update failed due to the following violations: {violations}.")
221 |         raise ValueError(" ".join(violations))
222 | 
223 | 
224 | async def generate_agenda(
225 |     language_model: LanguageModel,
226 |     definition: ConversationGuide,
227 |     chat_history: Conversation,
228 |     current_agenda: Agenda,
229 |     artifact: dict[str, Any],
230 |     resource: ConversationResource,
231 |     max_retries: int = 2,
232 | ) -> tuple[Agenda, bool]:
233 |     # STEP 1: Generate an updated agenda.
234 | 
235 |     # If there is a resource constraint and there's more than one turn left,
236 |     # include additional constraint instructions.
237 |     total_resource_phrase = ""
238 |     ample_time_phrase = ""
239 |     if resource.resource_constraint and resource.elapsed_units and resource.remaining_units > 1:
240 |         match resource.resource_constraint.mode:
241 |             case ResourceConstraintMode.MAXIMUM:
242 |                 total_resource_phrase = f"does not exceed the remaining turns ({resource.remaining_units})."
243 |             case ResourceConstraintMode.EXACT:
244 |                 total_resource_phrase = (
245 |                     f"is equal to the remaining turns ({resource.remaining_units}). Do not leave any turns unallocated."
246 |                 )
247 |                 ample_time_phrase = (
248 |                     "If you have many turns remaining, instead of including wrap-up items or repeating "
249 |                     "topics, you should include items that increase the breadth and/or depth of the conversation "
250 |                     'in a way that\'s directly relevant to the artifact (e.g. "collect additional details about X", '
251 |                     '"ask for clarification about Y", "explore related topic Z", etc.).'
252 |                 )
253 | 
254 |     completion_args = {
255 |         "model": "gpt-4o",
256 |         "messages": [
257 |             create_system_message(
258 |                 GENERATE_AGENDA_TEMPLATE,
259 |                 {
260 |                     "ample_time_phrase": ample_time_phrase,
261 |                     "artifact_schema": definition.artifact_schema,
262 |                     "context": definition.conversation_context,
263 |                     "current_state_description": definition.conversation_flow,
264 |                     "remaining_resource": resource.remaining_units,
265 |                     "resource_instructions": resource_instructions(resource),
266 |                     "rules": definition.rules,
267 |                     "total_resource_phrase": total_resource_phrase,
268 |                 },
269 |             ),
270 |             create_user_message(
271 |                 (
272 |                     "Conversation history:\n"
273 |                     "{{ chat_history }}\n\n"
274 |                     "Latest agenda:\n"
275 |                     "{{ agenda_state }}\n\n"
276 |                     "Current state of the artifact:\n"
277 |                     "{{ artifact_state }}"
278 |                 ),
279 |                 {
280 |                     "chat_history": str(chat_history),
281 |                     "agenda_state": agenda_phrase(current_agenda),
282 |                     "artifact_state": get_artifact_for_prompt(artifact),
283 |                 },
284 |             ),
285 |         ],
286 |         "response_format": Agenda,
287 |     }
288 | 
289 |     metadata = {}
290 |     logger.debug("Completion call.", extra=extra_data(make_completion_args_serializable(completion_args)))
291 |     metadata["completion_args"] = make_completion_args_serializable(completion_args)
292 |     try:
293 |         completion = await language_model.beta.chat.completions.parse(
294 |             **completion_args,
295 |         )
296 |         validate_completion(completion)
297 |         logger.debug("Completion response.", extra=extra_data({"completion": completion.model_dump()}))
298 |         metadata["completion"] = completion.model_dump()
299 |     except Exception as e:
300 |         completion_error = CompletionError(e)
301 |         metadata["completion_error"] = completion_error.message
302 |         logger.error(
303 |             completion_error.message,
304 |             extra=extra_data({"completion_error": completion_error.body, "metadata": metadata}),
305 |         )
306 |         raise completion_error from e
307 |     else:
308 |         new_agenda = cast(Agenda, completion.choices[0].message.parsed)
309 |         new_agenda.resource_constraint_mode = current_agenda.resource_constraint_mode
310 | 
311 |     # STEP 2: Validate/fix the updated agenda if necessary.
312 | 
313 |     previous_attempts = []
314 |     while len(previous_attempts) < max_retries:
315 |         try:
316 |             # Check resource constraints (will raise an error if violated).
317 |             if new_agenda.resource_constraint_mode is not None:
318 |                 check_item_constraints(
319 |                     new_agenda.resource_constraint_mode,
320 |                     new_agenda.items,
321 |                     resource.estimate_remaining_turns(),
322 |                 )
323 | 
324 |         except (ValidationError, ValueError) as e:
325 |             # Try again.
326 |             if isinstance(e, ValidationError):
327 |                 error_str = "; ".join([e.get("msg") for e in e.errors()])
328 |                 error_str = error_str.replace("; Input should be 'Unanswered'", " or input should be 'Unanswered'")
329 |             else:
330 |                 error_str = str(e)
331 | 
332 |             # Add it to our list of previous attempts.
333 |             previous_attempts.append((str(new_agenda.items), error_str))
334 | 
335 |             # Generate a new agenda.
336 |             logger.info(f"Attempting to fix the agenda error. Attempt {len(previous_attempts)}.")
337 |             llm_formatted_attempts = "\n".join([
338 |                 f"Attempt: {attempt}\nError: {error}" for attempt, error in previous_attempts
339 |             ])
340 |             possibly_fixed_agenda = await fix_agenda_error(language_model, llm_formatted_attempts, chat_history)
341 |             if possibly_fixed_agenda is None:
342 |                 raise ValueError("Invalid response from the LLM.")
343 |             new_agenda = possibly_fixed_agenda
344 |             continue
345 |         else:
346 |             is_done = True if len(new_agenda.items) == 0 else False
347 |             logger.info("Agenda updated successfully", extra=extra_data(new_agenda))
348 |             return new_agenda, is_done
349 | 
350 |     logger.error(f"Failed to update agenda after {max_retries} attempts.")
351 | 
352 |     # Let's keep going anyway.
353 |     return current_agenda, False
354 | 
```

--------------------------------------------------------------------------------
/libraries/python/chat-context-toolkit/test/history/test_history.py:
--------------------------------------------------------------------------------

```python
  1 | from uuid import uuid4
  2 | 
  3 | import pytest
  4 | from chat_context_toolkit.history import (
  5 |     HistoryMessageProtocol,
  6 |     NewTurn,
  7 |     OpenAIHistoryMessageParam,
  8 |     apply_budget_to_history_messages,
  9 | )
 10 | from openai.types.chat import (
 11 |     ChatCompletionAssistantMessageParam,
 12 |     ChatCompletionMessageParam,
 13 |     ChatCompletionToolMessageParam,
 14 |     ChatCompletionUserMessageParam,
 15 | )
 16 | 
 17 | 
 18 | class Message:
 19 |     def __init__(
 20 |         self,
 21 |         openai_message: OpenAIHistoryMessageParam,
 22 |         abbreviated_message: OpenAIHistoryMessageParam | None = None,
 23 |     ):
 24 |         self.id = uuid4().hex
 25 |         self.openai_message = openai_message
 26 |         self.abbreviated_openai_message = abbreviated_message or openai_message
 27 | 
 28 | 
 29 | def user_message(content: str, abbreviated_content: str | None = None) -> HistoryMessageProtocol:
 30 |     full_msg = ChatCompletionUserMessageParam(role="user", content=content)
 31 |     abbreviated_msg = (
 32 |         ChatCompletionUserMessageParam(role="user", content=abbreviated_content)
 33 |         if abbreviated_content is not None
 34 |         else full_msg
 35 |     )
 36 |     return Message(openai_message=full_msg, abbreviated_message=abbreviated_msg)
 37 | 
 38 | 
 39 | def assistant_message(
 40 |     content: str, abbreviated_content: str | None = None, tool_calls: list = []
 41 | ) -> HistoryMessageProtocol:
 42 |     full_msg = ChatCompletionAssistantMessageParam(role="assistant", content=content)
 43 |     if tool_calls:
 44 |         full_msg["tool_calls"] = tool_calls
 45 |     abbreviated_msg = (
 46 |         ChatCompletionAssistantMessageParam(role="assistant", content=abbreviated_content)
 47 |         if abbreviated_content is not None
 48 |         else full_msg
 49 |     )
 50 |     if tool_calls:
 51 |         abbreviated_msg["tool_calls"] = tool_calls
 52 |     return Message(openai_message=full_msg, abbreviated_message=abbreviated_msg)
 53 | 
 54 | 
 55 | def tool_message(tool_call_id: str, content: str) -> HistoryMessageProtocol:
 56 |     full_msg = ChatCompletionToolMessageParam(role="tool", tool_call_id=tool_call_id, content=content)
 57 |     return Message(openai_message=full_msg, abbreviated_message=full_msg)
 58 | 
 59 | 
 60 | class MockMessageProvider:
 61 |     def __init__(self, messages: list[HistoryMessageProtocol]):
 62 |         self.messages = messages
 63 | 
 64 |     async def __call__(self) -> list[HistoryMessageProtocol]:
 65 |         return self.messages
 66 | 
 67 | 
 68 | def token_counter(messages: list[ChatCompletionMessageParam]) -> int:
 69 |     """Simple token counter that uses string length as token count."""
 70 |     return sum(len(str(message.get("content") or "")) for message in messages)
 71 | 
 72 | 
 73 | async def test_empty_messages():
 74 |     """Test with empty message list."""
 75 |     message_provider = MockMessageProvider([])
 76 | 
 77 |     result = await apply_budget_to_history_messages(
 78 |         turn=NewTurn(50),
 79 |         token_budget=100,
 80 |         token_counter=token_counter,
 81 |         message_provider=message_provider,
 82 |     )
 83 | 
 84 |     assert result.messages == []
 85 | 
 86 | 
 87 | async def test_messages_within_total_budget():
 88 |     """Test when all messages fit within total budget."""
 89 |     messages = [
 90 |         user_message("hello"),  # 5 tokens
 91 |         user_message("hi there"),  # 8 tokens
 92 |         user_message("bye"),  # 3 tokens
 93 |     ]
 94 | 
 95 |     message_provider = MockMessageProvider(messages)
 96 | 
 97 |     result = await apply_budget_to_history_messages(
 98 |         turn=NewTurn(10),
 99 |         token_budget=20,
100 |         token_counter=token_counter,
101 |         message_provider=message_provider,
102 |     )
103 | 
104 |     expected = [msg.openai_message for msg in messages]
105 |     assert result.messages == expected
106 | 
107 | 
108 | async def test_messages_exceed_budget_abbreviation_needed():
109 |     """Test when messages exceed budget and require abbreviation."""
110 |     messages = [
111 |         user_message("this is a very long message", abbreviated_content="short"),  # 28 -> 5 tokens
112 |         assistant_message("hello world"),  # 11 tokens
113 |         user_message("bye"),  # 3 tokens
114 |     ]
115 | 
116 |     message_provider = MockMessageProvider(messages)
117 | 
118 |     result = await apply_budget_to_history_messages(
119 |         turn=NewTurn(15),
120 |         token_budget=20,
121 |         token_counter=token_counter,
122 |         message_provider=message_provider,
123 |     )
124 | 
125 |     # Should abbreviate the first message and keep others full
126 |     expected = [
127 |         ChatCompletionUserMessageParam(role="user", content="short"),  # abbreviated
128 |         ChatCompletionAssistantMessageParam(role="assistant", content="hello world"),  # full
129 |         ChatCompletionUserMessageParam(role="user", content="bye"),  # full
130 |     ]
131 |     assert result.messages == expected
132 | 
133 | 
134 | async def test_high_priority_messages_only():
135 |     """Test when only high priority messages fit within budget."""
136 |     messages = [
137 |         user_message("very long message that should be dropped", abbreviated_content="drop"),  # 40 -> 4 tokens
138 |         user_message("another long message to drop", abbreviated_content="drop2"),  # 30 -> 5 tokens
139 |         assistant_message("keep this"),  # 9 tokens
140 |         user_message("keep too"),  # 8 tokens
141 |     ]
142 | 
143 |     message_provider = MockMessageProvider(messages)
144 | 
145 |     result = await apply_budget_to_history_messages(
146 |         turn=NewTurn(18),
147 |         token_budget=20,
148 |         token_counter=token_counter,
149 |         message_provider=message_provider,
150 |     )
151 | 
152 |     # Should only keep the high priority messages (last 2)
153 |     expected = [
154 |         ChatCompletionAssistantMessageParam(role="assistant", content="keep this"),
155 |         ChatCompletionUserMessageParam(role="user", content="keep too"),
156 |     ]
157 |     assert result.messages == expected
158 | 
159 | 
160 | async def test_no_messages_fit_budget_raises_error():
161 |     """Test when no messages fit within budget, should raise ValueError."""
162 |     messages = [
163 |         user_message("this message is way too long for the budget"),  # 43 tokens
164 |         user_message("this one is also too long"),  # 25 tokens
165 |     ]
166 | 
167 |     message_provider = MockMessageProvider(messages)
168 | 
169 |     with pytest.raises(ValueError, match="no messages fit within the token budget"):
170 |         await apply_budget_to_history_messages(
171 |             turn=NewTurn(5),
172 |             token_budget=10,
173 |             token_counter=token_counter,
174 |             message_provider=message_provider,
175 |         )
176 | 
177 | 
178 | async def test_progressive_abbreviation_and_truncation():
179 |     """Test the full workflow of abbreviation and truncation."""
180 |     messages = [
181 |         user_message("first very long message", abbreviated_content="first"),  # 24 -> 5 tokens
182 |         user_message("second very long message", abbreviated_content="second"),  # 25 -> 6 tokens
183 |         user_message("third message", abbreviated_content="third"),  # 13 -> 5 tokens
184 |         user_message("fourth message", abbreviated_content="fourth"),  # 14 -> 6 tokens
185 |         user_message("fifth short"),  # 11 tokens
186 |     ]
187 | 
188 |     message_provider = MockMessageProvider(messages)
189 | 
190 |     result = await apply_budget_to_history_messages(
191 |         turn=NewTurn(20),
192 |         token_budget=30,
193 |         token_counter=token_counter,
194 |         message_provider=message_provider,
195 |     )
196 | 
197 |     # Should process messages to fit within budget
198 |     # High priority: last 3 messages (5 + 6 + 11 = 22 > 20, so last 2: 6 + 11 = 17)
199 |     # Low priority budget: 30 - 17 = 13 tokens
200 |     # Should abbreviate some low priority messages to fit
201 |     assert len(result.messages) > 0
202 |     assert sum(len(str(msg.get("content") or "")) for msg in result.messages) <= 30
203 | 
204 | 
205 | async def test_tool_message_pairing():
206 |     """Test that tool calls and results are properly paired and ordered."""
207 |     tool_calls = [{"id": "call_123", "type": "function", "function": {"name": "test", "arguments": "{}"}}]
208 | 
209 |     messages = [
210 |         user_message("call a tool"),
211 |         assistant_message("I'll call a tool", tool_calls=tool_calls),
212 |         tool_message("call_123", "tool result"),
213 |         assistant_message("done"),
214 |     ]
215 | 
216 |     message_provider = MockMessageProvider(messages)
217 | 
218 |     result = await apply_budget_to_history_messages(
219 |         turn=NewTurn(50),
220 |         token_budget=100,
221 |         token_counter=token_counter,
222 |         message_provider=message_provider,
223 |     )
224 | 
225 |     # Should preserve tool call pairing and ordering
226 |     messages = result.messages
227 |     assert len(messages) == 4
228 |     assert messages[0]["role"] == "user"
229 |     assert messages[1]["role"] == "assistant"
230 |     assert messages[2]["role"] == "tool"
231 |     assert messages[3]["role"] == "assistant"
232 | 
233 | 
234 | async def test_large_budget_all_messages_included():
235 |     """Test with very large budget that should include all messages."""
236 |     messages = [
237 |         user_message("first"),
238 |         user_message("second"),
239 |         user_message("third"),
240 |         user_message("fourth"),
241 |     ]
242 | 
243 |     message_provider = MockMessageProvider(messages)
244 | 
245 |     result = await apply_budget_to_history_messages(
246 |         turn=NewTurn(500),
247 |         token_budget=1000,
248 |         token_counter=token_counter,
249 |         message_provider=message_provider,
250 |     )
251 | 
252 |     expected = [msg.openai_message for msg in messages]
253 |     assert result.messages == expected
254 | 
255 | 
256 | async def test_zero_budget_scenarios():
257 |     """Test edge cases with zero budget."""
258 |     messages = [user_message("hello")]
259 |     message_provider = MockMessageProvider(messages)
260 | 
261 |     with pytest.raises(ValueError, match="no messages fit within the token budget"):
262 |         await apply_budget_to_history_messages(
263 |             turn=NewTurn(0),
264 |             token_budget=1,
265 |             token_counter=token_counter,
266 |             message_provider=message_provider,
267 |         )
268 | 
269 | 
270 | async def test_session_step_pins_high_priority_window() -> None:
271 |     """Test that SessionStep pins the high priority window across multiple calls within an agent turn."""
272 |     # Start with some older messages that should be low priority
273 |     initial_messages = [
274 |         user_message("old message 1", abbreviated_content="old1"),  # 13 -> 4 tokens
275 |         user_message("old message 2", abbreviated_content="old2"),  # 13 -> 4 tokens
276 |         user_message("old response", abbreviated_content="old"),  # 12 -> 3 tokens
277 |     ]
278 | 
279 |     # Messages that will be added during the agent turn (should be high priority)
280 |     agent_turn_messages = [
281 |         user_message("user request 1"),  # 14 tokens
282 |         assistant_message("agent thinking"),  # 14 tokens
283 |         assistant_message("agent response"),  # 14 tokens
284 |     ]
285 | 
286 |     # Simulate an agent turn where multiple calls to get_history_messages happen
287 |     turn = NewTurn(35)  # 35 high priority budget
288 | 
289 |     # First call in the agent turn - only user request present
290 |     provider1 = MockMessageProvider(initial_messages + agent_turn_messages[:1])
291 |     result1 = await apply_budget_to_history_messages(
292 |         turn=turn,
293 |         token_budget=50,
294 |         token_counter=token_counter,
295 |         message_provider=provider1,
296 |     )
297 | 
298 |     expected = [
299 |         ChatCompletionUserMessageParam(role="user", content="old1"),  # low priority, abbreviated
300 |         ChatCompletionUserMessageParam(role="user", content="old message 2"),  # preserved
301 |         ChatCompletionUserMessageParam(role="user", content="old response"),  # preserved
302 |         ChatCompletionUserMessageParam(role="user", content="user request 1"),  # preserved
303 |     ]
304 | 
305 |     assert result1.messages == expected
306 | 
307 |     # Second call - add agent thinking message
308 |     provider2 = MockMessageProvider(initial_messages + agent_turn_messages[:2])
309 |     result2 = await apply_budget_to_history_messages(
310 |         turn=turn,
311 |         token_budget=50,
312 |         token_counter=token_counter,
313 |         message_provider=provider2,
314 |     )
315 | 
316 |     expected = [
317 |         ChatCompletionUserMessageParam(role="user", content="old1"),  # low priority, abbreviated
318 |         ChatCompletionUserMessageParam(role="user", content="old2"),  # low priority, abbreviated
319 |         ChatCompletionUserMessageParam(role="user", content="old response"),  # low priority, not abbreviated
320 |         ChatCompletionUserMessageParam(role="user", content="user request 1"),  # preserved
321 |         ChatCompletionAssistantMessageParam(role="assistant", content="agent thinking"),  # preserved
322 |     ]
323 | 
324 |     assert result2.messages == expected
325 | 
326 |     # Third call - add final agent response
327 |     provider3 = MockMessageProvider(initial_messages + agent_turn_messages)
328 |     result3 = await apply_budget_to_history_messages(
329 |         turn=turn,
330 |         token_budget=50,
331 |         token_counter=token_counter,
332 |         message_provider=provider3,
333 |     )
334 | 
335 |     # The high priority window should be pinned, so old messages should be abbreviated
336 |     # and agent turn messages should be preserved
337 |     expected = [
338 |         # first one is low priority and truncated - so not included here
339 |         ChatCompletionUserMessageParam(role="user", content="old2"),  # low priority, abbreviated
340 |         ChatCompletionUserMessageParam(role="user", content="old"),  # low priority, abbreviated
341 |         ChatCompletionUserMessageParam(role="user", content="user request 1"),  # high priority, preserved
342 |         ChatCompletionAssistantMessageParam(role="assistant", content="agent thinking"),  # preserved
343 |         ChatCompletionAssistantMessageParam(role="assistant", content="agent response"),  # preserved
344 |     ]
345 | 
346 |     assert result3.messages == expected
347 | 
348 | 
349 | async def test_high_priority_with_overflow() -> None:
350 |     """Test when high priority budget equals total budget, then messages overflow requiring truncation."""
351 |     # Historical messages that would normally be low priority
352 |     historical_messages = [
353 |         user_message("historical message one", abbreviated_content="hist1"),  # 22 -> 5 tokens
354 |         user_message("historical message two", abbreviated_content="hist2"),  # 22 -> 5 tokens
355 |     ]
356 | 
357 |     # Agent turn messages that will be high priority but exceed the budget
358 |     agent_turn_messages = [
359 |         user_message("agent turn request", abbreviated_content="never"),  # 18 tokens
360 |         assistant_message("agent processing this request", abbreviated_content="never"),  # 29 tokens
361 |         assistant_message("agent final response here", abbreviated_content="never"),  # 25 tokens
362 |         user_message("follow up question", abbreviated_content="never"),  # 18 tokens
363 |     ]
364 | 
365 |     turn = NewTurn(72)  # High priority = total budget
366 | 
367 |     # First call - establish the high priority window with historical + first agent message
368 |     provider1 = MockMessageProvider(historical_messages + agent_turn_messages[:1])
369 |     await apply_budget_to_history_messages(
370 |         turn=turn,
371 |         token_budget=72,
372 |         token_counter=token_counter,
373 |         message_provider=provider1,
374 |     )
375 | 
376 |     # Second call - add more agent messages
377 |     provider2 = MockMessageProvider(historical_messages + agent_turn_messages[:2])
378 |     await apply_budget_to_history_messages(
379 |         turn=turn,
380 |         token_budget=72,
381 |         token_counter=token_counter,
382 |         message_provider=provider2,
383 |     )
384 | 
385 |     # Final call - add all agent messages, which will overflow the budget
386 |     provider3 = MockMessageProvider(historical_messages + agent_turn_messages)
387 |     result3 = await apply_budget_to_history_messages(
388 |         turn=turn,
389 |         token_budget=72,
390 |         token_counter=token_counter,
391 |         message_provider=provider3,
392 |     )
393 | 
394 |     # Since high priority budget = total budget, there's no room for low priority messages
395 |     # The system should truncate high priority messages as needed
396 |     expected = [
397 |         # all (2) historical messages and first (1) agent turn messages should have been truncated
398 |         ChatCompletionAssistantMessageParam(role="assistant", content="agent processing this request"),  # preserved
399 |         ChatCompletionAssistantMessageParam(role="assistant", content="agent final response here"),  # preserved
400 |         ChatCompletionUserMessageParam(role="user", content="follow up question"),  # preserved
401 |     ]
402 | 
403 |     assert result3.messages == expected
404 | 
405 | 
406 | async def test_high_priority_for_turn_with_large_latest_message():
407 |     """
408 |     Test that high priority window is correctly set to include the latest message, even when the latest message is too
409 |     large to fit in the high priority token count.
410 |     """
411 |     # Historical messages that should be abbreviated
412 |     historical_messages = [
413 |         user_message("historical message one is very long"),  # 35 tokens
414 |         user_message("historical message two is very long"),  # 35 tokens
415 |     ]
416 | 
417 |     # Latest agent turn message that is large and should be high priority
418 |     latest_message = user_message(
419 |         "a high priority message",
420 |         abbreviated_content="long response",
421 |     )  # 23 tokens
422 | 
423 |     # High priority token count less than latest message size
424 |     turn = NewTurn(high_priority_token_count=20)
425 | 
426 |     # Call with historical messages and the large latest message
427 |     all_messages = historical_messages + [latest_message]
428 |     provider = MockMessageProvider(all_messages)
429 | 
430 |     result = await apply_budget_to_history_messages(
431 |         turn=turn,
432 |         token_budget=50,  # Forces a truncation pass of historical/low-priority messages
433 |         token_counter=token_counter,
434 |         message_provider=provider,
435 |     )
436 | 
437 |     # Should result in latest message being preserved and historical messages abbreviated and truncated
438 |     expected = [
439 |         # all historical messages should be truncated
440 |         ChatCompletionUserMessageParam(role="user", content="a high priority message"),  # preserved
441 |     ]
442 | 
443 |     assert result.messages == expected
444 | 
445 |     all_messages += [
446 |         user_message(
447 |             "1 high priority message",
448 |             abbreviated_content="long response",
449 |         )
450 |     ]
451 |     provider = MockMessageProvider(all_messages)
452 | 
453 |     result = await apply_budget_to_history_messages(
454 |         turn=turn,
455 |         token_budget=50,  # Forces a truncation pass of historical/low-priority messages
456 |         token_counter=token_counter,
457 |         message_provider=provider,
458 |     )
459 | 
460 |     # Should result in latest two messages being preserved, as they are under the total budget
461 |     expected = [
462 |         # all historical messages should be truncated
463 |         ChatCompletionUserMessageParam(role="user", content="a high priority message"),  # preserved
464 |         ChatCompletionUserMessageParam(role="user", content="1 high priority message"),  # preserved
465 |     ]
466 | 
467 |     assert result.messages == expected
468 | 
469 |     all_messages += [
470 |         user_message(
471 |             "2 high priority message",
472 |             abbreviated_content="long response",
473 |         )
474 |     ]
475 |     provider = MockMessageProvider(all_messages)
476 | 
477 |     result = await apply_budget_to_history_messages(
478 |         turn=turn,
479 |         token_budget=50,  # Forces a truncation pass of historical/low-priority messages
480 |         token_counter=token_counter,
481 |         message_provider=provider,
482 |     )
483 | 
484 |     # Should result in latest two messages being preserved, as they are under the total budget
485 |     expected = [
486 |         # all historical messages and the first turn message should be truncated
487 |         ChatCompletionUserMessageParam(role="user", content="1 high priority message"),  # preserved
488 |         ChatCompletionUserMessageParam(role="user", content="2 high priority message"),  # preserved
489 |     ]
490 | 
491 |     assert result.messages == expected
492 | 
```
Page 92/145FirstPrevNextLast