This is page 112 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
--------------------------------------------------------------------------------
/assistants/knowledge-transfer-assistant/assistant/respond.py:
--------------------------------------------------------------------------------
```python
1 | import re
2 | import time
3 | from textwrap import dedent
4 | from typing import Any, Dict, List
5 |
6 | import openai_client
7 | from assistant_extensions.attachments import AttachmentsExtension
8 | from openai import BaseModel
9 | from openai.types.chat import (
10 | ChatCompletionAssistantMessageParam,
11 | ChatCompletionMessageParam,
12 | ChatCompletionSystemMessageParam,
13 | ChatCompletionUserMessageParam,
14 | )
15 | from openai_client import num_tokens_from_messages
16 | from openai_client.completion import message_content_from_completion
17 | from openai_client.tools import complete_with_tool_calls
18 | from pydantic import Field
19 | from semantic_workbench_api_model.workbench_model import (
20 | ConversationMessage,
21 | ConversationParticipantList,
22 | MessageType,
23 | NewConversationMessage,
24 | )
25 | from semantic_workbench_assistant.assistant_app import (
26 | ConversationContext,
27 | )
28 |
29 | from .agentic.analysis import detect_information_request_needs
30 | from .agentic.coordinator_support import CoordinatorSupport
31 | from .common import detect_assistant_role
32 | from .config import assistant_config
33 | from .data import RequestStatus
34 | from .domain import KnowledgeTransferManager
35 | from .logging import logger
36 | from .storage import ShareStorage
37 | from .storage_models import ConversationRole, CoordinatorConversationMessage
38 | from .string_utils import Context, ContextStrategy, Instructions, Prompt, TokenBudget
39 | from .tools import ShareTools
40 | from .ui_tabs.common import get_priority_emoji, get_status_emoji
41 | from .utils import load_text_include
42 |
43 | SILENCE_TOKEN = "{{SILENCE}}"
44 |
45 |
46 | def format_message(participants: ConversationParticipantList, message: ConversationMessage) -> str:
47 | """Consistent formatter that includes the participant name for multi-participant and name references"""
48 | conversation_participant = next(
49 | (participant for participant in participants.participants if participant.id == message.sender.participant_id),
50 | None,
51 | )
52 | participant_name = conversation_participant.name if conversation_participant else "unknown"
53 | message_datetime = message.timestamp.strftime("%Y-%m-%d %H:%M:%S")
54 | return f"[{participant_name} - {message_datetime}]: {message.content}"
55 |
56 |
57 | class CoordinatorOutput(BaseModel):
58 | """
59 | Attributes:
60 | response: The response from the assistant.
61 | next_step_suggestion: Help for the coordinator to understand what to do next. A great way to progressively reveal the knowledge transfer process.
62 | """
63 |
64 | response: str = Field(
65 | description="The response from the assistant. The response should not duplicate information from the excerpt but may refer to it.",
66 | )
67 | next_step_suggestion: str = Field(
68 | description="Help for the coordinator to understand what to do next. A great way to progressively reveal the knowledge transfer process. The audience is the coordinator, so this should be a suggestion for them to take action. Do NOT use this field to communicate what you, the assistant, are going to do next. Assume the coordinator has not yet used this assistant before and make sure to explain concepts such as the knowledge brief and learning outcomes clearly the first time you mention them.",
69 | )
70 |
71 | model_config = {
72 | "extra": "forbid" # This sets additionalProperties=false in the schema
73 | }
74 |
75 |
76 | class TeamOutput(BaseModel):
77 | """
78 | Attributes:
79 | citations: A list of citations from which the response is generated. There should always be at least one citation, but it can be empty if the assistant has no relevant information to cite.
80 | excerpt: A verbatim excerpt from one of the cited works that illustrates why this response was given. It should have enough context to get a good idea of what's in that part of the cited work. DO NOT excerpt from CONVERSATION or DIGEST, only from attachments. If there is no relevant excerpt, this will be None. If there is special formatting in the excerpt, remove it as the excerpt will be displayed in quotes in a chat message and should not contain any formatting that would not be supported in a chat message (e.g. markdown).
81 | next_step_suggestion: Suggest more areas to explore using content from the knowledge digest to ensure your conversation covers all of the relevant information.
82 | """
83 |
84 | citations: list[str] = Field(
85 | description="A list of citations from which the response is generated. There should always be at least one citation, but it can be empty if the assistant has no relevant information to cite.",
86 | )
87 | excerpt: str | None = Field(
88 | description="A verbatim excerpt from one of the cited works that illustrates why this response was given. It should have enough context to get a good idea of what's in that part of the cited work. DO NOT excerpt from CONVERSATION or KNOWLEDGE_DIGEST, only from attachments. If there is no relevant excerpt, this will be None. If there is special formatting in the excerpt, remove it as the excerpt will be displayed in quotes in a chat message and should not contain any formatting that would not be supported in a chat message (e.g. markdown).",
89 | )
90 | response: str = Field(
91 | description="The response from the assistant. The response should not duplicate information from the excerpt but may refer to it.",
92 | )
93 | next_step_suggestion: str = Field(
94 | description="Suggest more areas to explore using content from the knowledge digest to ensure your conversation covers all of the relevant information. For example: 'Would you like to explore ... next?'.",
95 | )
96 |
97 | model_config = {
98 | "extra": "forbid" # This sets additionalProperties=false in the schema
99 | }
100 |
101 |
102 | async def respond_to_conversation(
103 | context: ConversationContext,
104 | new_message: ConversationMessage,
105 | attachments_extension: AttachmentsExtension,
106 | metadata: Dict[str, Any],
107 | ) -> None:
108 | """
109 | Respond to a conversation message.
110 | """
111 | if "debug" not in metadata:
112 | metadata["debug"] = {}
113 |
114 | # Config
115 | config = await assistant_config.get(context.assistant)
116 | model = config.request_config.openai_model
117 |
118 | # Requirements
119 | role = await detect_assistant_role(context)
120 | metadata["debug"]["role"] = role
121 | share_id = await KnowledgeTransferManager.get_share_id(context)
122 | if not share_id:
123 | raise ValueError("Project ID not found in context")
124 |
125 | token_budget = TokenBudget(config.request_config.max_tokens)
126 |
127 | ##
128 | ## INSTRUCTIONS
129 | ##
130 |
131 | # Add role-specific instructions.
132 | if role == ConversationRole.COORDINATOR:
133 | assistant_role = config.prompt_config.coordinator_role
134 | role_specific_instructions = config.prompt_config.coordinator_instructions
135 | else:
136 | assistant_role = config.prompt_config.team_role
137 | role_specific_instructions = config.prompt_config.team_instructions
138 | instructions = Instructions(role_specific_instructions)
139 |
140 | # Add knowledge digest instructions.
141 | instructions.add_subsection(
142 | Instructions(
143 | load_text_include("knowledge_digest_instructions.txt"),
144 | "Assistant's Knowledge Digest",
145 | )
146 | )
147 |
148 | # If this is a multi-participant conversation, add a note about the participants.
149 | participants = await context.get_participants(include_inactive=True)
150 | if len(participants.participants) > 2:
151 | participant_text = (
152 | "\n\n"
153 | f"There are {len(participants.participants)} participants in the conversation,"
154 | " including you as the assistant and the following users:"
155 | + ",".join([
156 | f' "{participant.name}"'
157 | for participant in participants.participants
158 | if participant.id != context.assistant.id
159 | ])
160 | + "\n\nYou do not need to respond to every message. Do not respond if the last thing said was a closing"
161 | " statement such as 'bye' or 'goodbye', or just a general acknowledgement like 'ok' or 'thanks'. Do not"
162 | f' respond as another user in the conversation, only as "{context.assistant.name}".'
163 | " Sometimes the other users need to talk amongst themselves and that is ok. If the conversation seems to"
164 | f' be directed at you or the general audience, go ahead and respond.\n\nSay "{SILENCE_TOKEN}" to skip'
165 | " your turn."
166 | )
167 | instructions.add_subsection(Instructions(participant_text, "Multi-participant conversation instructions"))
168 |
169 | prompt = Prompt(
170 | role=assistant_role,
171 | instructions=instructions,
172 | context_strategy=ContextStrategy.MULTI,
173 | )
174 | if role == ConversationRole.TEAM:
175 | prompt.output_format = "Respond as JSON with your response in the `response` field and all citations in the `citations` field. In the `next_step_suggestion` field, suggest more areas to explore using content from the assistant whiteboard to ensure your conversation covers all of the relevant information."
176 |
177 | ###
178 | ### Context
179 | ###
180 |
181 | # Project info
182 | share_info = ShareStorage.read_share_info(share_id)
183 | if share_info:
184 | data = share_info.model_dump()
185 |
186 | # Delete fields that are not relevant to the knowledge transfer assistant.
187 | # FIXME: Reintroduce these properly.
188 | if "state" in data:
189 | del data["state"]
190 | if "progress_percentage" in data:
191 | del data["progress_percentage"]
192 | if "completed_criteria" in data:
193 | del data["completed_criteria"]
194 | if "total_criteria" in data:
195 | del data["total_criteria"]
196 | if "lifecycle" in data:
197 | del data["lifecycle"]
198 |
199 | share_info_text = share_info.model_dump_json(indent=2)
200 | prompt.contexts.append(Context("Knowledge Info", share_info_text))
201 |
202 | # Brief
203 | briefing = ShareStorage.read_knowledge_brief(share_id)
204 | brief_text = ""
205 | if briefing:
206 | brief_text = f"**Title:** {briefing.title}\n**Description:** {briefing.content}"
207 | prompt.contexts.append(
208 | Context(
209 | "Knowledge Brief",
210 | brief_text,
211 | )
212 | )
213 |
214 | # Audience (for coordinators to understand target audience)
215 | if role == ConversationRole.COORDINATOR and share_info and share_info.audience:
216 | audience_context = share_info.audience
217 | if not share_info.is_intended_to_accomplish_outcomes:
218 | audience_context += "\n\n**Note:** This knowledge package is intended for general exploration, not specific learning outcomes."
219 |
220 | prompt.contexts.append(
221 | Context(
222 | "Target Audience",
223 | audience_context,
224 | "Description of the intended audience and their existing knowledge level for this knowledge transfer.",
225 | )
226 | )
227 |
228 | # Learning objectives
229 | share = ShareStorage.read_share(share_id)
230 | if share and share.learning_objectives:
231 | learning_objectives_text = ""
232 | conversation_id = str(context.id)
233 |
234 | # Show progress based on role
235 | if role == ConversationRole.COORDINATOR:
236 | # Coordinator sees overall progress across all team members
237 | achieved_overall, total_overall = share.get_overall_completion()
238 | learning_objectives_text += (
239 | f"Overall Progress: {achieved_overall}/{total_overall} outcomes achieved by team members\n\n"
240 | )
241 | else:
242 | # Team member sees their personal progress
243 | if conversation_id in share.team_conversations:
244 | achieved_personal, total_personal = share.get_completion_for_conversation(conversation_id)
245 | progress_pct = int((achieved_personal / total_personal * 100)) if total_personal > 0 else 0
246 | learning_objectives_text += (
247 | f"My Progress: {achieved_personal}/{total_personal} outcomes achieved ({progress_pct}%)\n\n"
248 | )
249 |
250 | for i, objective in enumerate(share.learning_objectives):
251 | brief_text += f"{i + 1}. **{objective.name}** - {objective.description}\n"
252 | if objective.learning_outcomes:
253 | for criterion in objective.learning_outcomes:
254 | if role == ConversationRole.COORDINATOR:
255 | # Show if achieved by any team member
256 | achieved_by_any = any(
257 | share.is_outcome_achieved_by_conversation(criterion.id, conv_id)
258 | for conv_id in share.team_conversations.keys()
259 | )
260 | check = "✅" if achieved_by_any else "⬜"
261 | else:
262 | # Show if achieved by this team member
263 | achieved_by_me = share.is_outcome_achieved_by_conversation(criterion.id, conversation_id)
264 | check = "✅" if achieved_by_me else "⬜"
265 |
266 | learning_objectives_text += f" {check} {criterion.description}\n"
267 | prompt.contexts.append(
268 | Context(
269 | "Learning Objectives",
270 | learning_objectives_text,
271 | )
272 | )
273 |
274 | # Knowledge digest
275 | knowledge_digest = ShareStorage.read_knowledge_digest(share_id)
276 | if knowledge_digest and knowledge_digest.content:
277 | prompt.contexts.append(
278 | Context("Knowledge digest", knowledge_digest.content, "The assistant-maintained knowledge digest.")
279 | )
280 |
281 | # Information requests
282 | all_requests = ShareStorage.get_all_information_requests(share_id)
283 | if role == ConversationRole.COORDINATOR:
284 | active_requests = [r for r in all_requests if r.status != RequestStatus.RESOLVED]
285 | if active_requests:
286 | coordinator_requests = "> 📋 **Use the request ID (not the title) with resolve_information_request()**\n\n"
287 | for req in active_requests[:10]: # Limit to 10 for brevity
288 | priority_emoji = get_priority_emoji(req.priority)
289 | status_emoji = get_status_emoji(req.status)
290 | coordinator_requests += f"{priority_emoji} **{req.title}** {status_emoji}\n"
291 | coordinator_requests += f" **Request ID:** `{req.request_id}`\n"
292 | coordinator_requests += f" **Description:** {req.description}\n\n"
293 |
294 | if len(active_requests) > 10:
295 | coordinator_requests += f'*...and {len(active_requests) - 10} more requests. Use get_share_info(info_type="requests") to see all.*\n'
296 | else:
297 | coordinator_requests = "No active information requests."
298 | prompt.contexts.append(
299 | Context(
300 | "Information Requests",
301 | coordinator_requests,
302 | )
303 | )
304 | else: # team role
305 | information_requests_info = ""
306 | my_requests = []
307 |
308 | # Filter for requests from this conversation that aren't resolved.
309 | my_requests = [
310 | r for r in all_requests if r.conversation_id == str(context.id) and r.status != RequestStatus.RESOLVED
311 | ]
312 |
313 | if my_requests:
314 | information_requests_info = ""
315 | for req in my_requests:
316 | information_requests_info += f"- **{req.title}** (ID: `{req.request_id}`, Priority: {req.priority})\n"
317 | else:
318 | information_requests_info = "No active information requests."
319 |
320 | prompt.contexts.append(
321 | Context(
322 | "Information Requests",
323 | information_requests_info,
324 | )
325 | )
326 |
327 | # Add next action suggestions for coordinator
328 | if role == ConversationRole.COORDINATOR:
329 | next_action_suggestion = await CoordinatorSupport.get_coordinator_next_action_suggestion(context)
330 | if next_action_suggestion:
331 | prompt.contexts.append(
332 | Context(
333 | "Suggested Next Actions",
334 | next_action_suggestion,
335 | "Actions the coordinator should consider taking based on the current knowledge transfer state.",
336 | )
337 | )
338 |
339 | # Calculate token count for all system messages so far.
340 | completion_messages = prompt.messages()
341 | token_budget.add(
342 | num_tokens_from_messages(
343 | model=model,
344 | messages=completion_messages,
345 | )
346 | )
347 |
348 | ###
349 | ### Coordinator conversation as an attachment.
350 | ###
351 |
352 | # Get the coordinator conversation and add it as an attachment.
353 | coordinator_conversation = ShareStorage.read_coordinator_conversation(share_id)
354 | if coordinator_conversation:
355 | # Limit messages to the configured max token count.
356 | total_coordinator_conversation_tokens = 0
357 | selected_coordinator_conversation_messages: List[CoordinatorConversationMessage] = []
358 | for msg in reversed(coordinator_conversation.messages):
359 | tokens = openai_client.num_tokens_from_string(msg.model_dump_json(), model=model)
360 | if (
361 | total_coordinator_conversation_tokens + tokens
362 | > config.request_config.coordinator_conversation_token_limit
363 | ):
364 | break
365 | selected_coordinator_conversation_messages.append(msg)
366 | total_coordinator_conversation_tokens += tokens
367 |
368 | # Create a new coordinator conversation system message with the selected messages.
369 | class CoordinatorMessageList(BaseModel):
370 | messages: List[CoordinatorConversationMessage] = Field(default_factory=list)
371 |
372 | selected_coordinator_conversation_messages.reverse()
373 | coordinator_message_list = CoordinatorMessageList(messages=selected_coordinator_conversation_messages)
374 | coordinator_conversation_message = ChatCompletionSystemMessageParam(
375 | role="system",
376 | content=(
377 | f"<ATTACHMENT><FILENAME>CoordinatorConversation.json</FILENAME><CONTENT>{coordinator_message_list.model_dump_json()}</CONTENT>"
378 | ),
379 | )
380 | completion_messages.append(coordinator_conversation_message)
381 |
382 | token_budget.add(
383 | num_tokens_from_messages(
384 | model=model,
385 | messages=[coordinator_conversation_message],
386 | )
387 | )
388 |
389 | ###
390 | ### ATTACHMENTS
391 | ###
392 |
393 | # TODO: A better pattern here might be to keep the attachments as user
394 | # in the proper flow of the conversation rather than as .
395 |
396 | # Generate the attachment messages.
397 | attachment_messages: List[ChatCompletionMessageParam] = openai_client.convert_from_completion_messages(
398 | await attachments_extension.get_completion_messages_for_attachments(
399 | context,
400 | config=config.attachments_config,
401 | )
402 | )
403 |
404 | # TODO: This will exceed the token limit if there are too many attachments.
405 | # We do give them a warning below, though, and tell them to remove
406 | # attachments if this happens.
407 |
408 | token_budget.add(
409 | num_tokens_from_messages(
410 | model=model,
411 | messages=attachment_messages,
412 | )
413 | )
414 | completion_messages.extend(attachment_messages)
415 |
416 | ###
417 | ### USER MESSAGE
418 | ###
419 |
420 | if new_message.sender.participant_id == context.assistant.id:
421 | user_message: ChatCompletionMessageParam = ChatCompletionAssistantMessageParam(
422 | role="assistant",
423 | content=format_message(participants, new_message),
424 | )
425 | else:
426 | user_message: ChatCompletionMessageParam = ChatCompletionUserMessageParam(
427 | role="user",
428 | content=format_message(participants, new_message),
429 | )
430 |
431 | token_budget.add(
432 | num_tokens_from_messages(
433 | model=model,
434 | messages=[user_message],
435 | )
436 | )
437 |
438 | ###
439 | ### HISTORY MESSAGES
440 | ###
441 |
442 | history_messages: list[ChatCompletionMessageParam] = []
443 | before_message_id = new_message.id
444 | history_token_budget = TokenBudget(token_budget.remaining())
445 |
446 | # Fetch messages from the workbench in batches that will fit our token budget.
447 | under_budget = True
448 | while under_budget:
449 | # Get a batch of messages
450 | messages_response = await context.get_messages(
451 | before=before_message_id,
452 | limit=100,
453 | message_types=[MessageType.chat],
454 | )
455 | messages_list = messages_response.messages
456 | if not messages_list or len(messages_list) == 0:
457 | break
458 | before_message_id = messages_list[0].id
459 |
460 | for msg in reversed(messages_list):
461 | if msg.sender.participant_id == context.assistant.id:
462 | # For assistant messages, include help suggestions as part of the message content
463 | message_content = format_message(participants, msg)
464 | if msg.metadata and "help" in msg.metadata:
465 | message_content += f"\n\n[Next step?: {msg.metadata['help']}]"
466 |
467 | current_message = ChatCompletionAssistantMessageParam(
468 | role="assistant",
469 | content=message_content,
470 | )
471 | else:
472 | current_message = ChatCompletionUserMessageParam(
473 | role="user",
474 | content=format_message(participants, msg),
475 | )
476 |
477 | current_message_tokens = num_tokens_from_messages(
478 | model=model,
479 | messages=[current_message],
480 | )
481 |
482 | if history_token_budget.fits(current_message_tokens):
483 | history_messages = [current_message] + history_messages
484 | history_token_budget.add(current_message_tokens)
485 | else:
486 | under_budget = False
487 | break
488 |
489 | if not under_budget:
490 | break
491 |
492 | # Add all chat messages.
493 | completion_messages.extend(history_messages)
494 | completion_messages.append(user_message)
495 |
496 | # Add a system message to indicate attachments are a part of the new message.
497 | if new_message.filenames and len(new_message.filenames) > 0:
498 | attachment_message = ChatCompletionSystemMessageParam(
499 | role="system",
500 | content=f"Attachment(s): {', '.join(new_message.filenames)}",
501 | )
502 | completion_messages.append(attachment_message)
503 | token_budget.add(
504 | num_tokens_from_messages(
505 | model=model,
506 | messages=[attachment_message],
507 | )
508 | )
509 |
510 | ##
511 | ## Final token count check
512 | ##
513 | token_counts = {"total": token_budget.used, "max": token_budget.budget}
514 | metadata["debug"]["token_usage"] = token_counts # For debug.
515 | metadata["token_counts"] = token_counts # For footer.
516 | if token_budget.remaining() < 0:
517 | raise ValueError(
518 | f"You've exceeded the token limit of {token_budget.budget} in this conversation "
519 | f"({token_budget.used}). Try removing some attachments."
520 | )
521 |
522 | # For team role, analyze message for possible information request needs.
523 | # Send a notification if we think it might be one.
524 | if role is ConversationRole.TEAM:
525 | detection_result = await detect_information_request_needs(context, new_message.content)
526 |
527 | if detection_result.get("is_information_request", False) and detection_result.get("confidence", 0) > 0.8:
528 | suggested_title = detection_result.get("potential_title", "")
529 | suggested_priority = detection_result.get("suggested_priority", "medium")
530 | potential_description = detection_result.get("potential_description", "")
531 | reason = detection_result.get("reason", "")
532 |
533 | suggestion = (
534 | f"**Potential _Information Request_ Detected**\n\n"
535 | f"It appears that you might need information from the knowledge coordinator. {reason}\n\n"
536 | f"Would you like me to create an information request?\n"
537 | f"**Title:** {suggested_title}\n"
538 | f"**Description:** {potential_description}\n"
539 | f"**Priority:** {suggested_priority}\n\n"
540 | )
541 |
542 | await context.send_messages(
543 | NewConversationMessage(
544 | content=suggestion,
545 | message_type=MessageType.notice,
546 | metadata={"debug": detection_result},
547 | )
548 | )
549 | metadata["debug"]["detection_result"] = detection_result
550 |
551 | ##
552 | ## MAKE THE LLM CALL
553 | ##
554 |
555 | async with openai_client.create_client(config.service_config) as client:
556 | try:
557 | completion_args = {
558 | "messages": completion_messages,
559 | "model": model,
560 | "max_tokens": config.request_config.response_tokens,
561 | "response_format": CoordinatorOutput if role == ConversationRole.COORDINATOR else TeamOutput,
562 | }
563 |
564 | share_tools = ShareTools(context, role)
565 | response_start_time = time.time()
566 | completion_response, additional_messages = await complete_with_tool_calls(
567 | async_client=client,
568 | completion_args=completion_args,
569 | tool_functions=share_tools.tool_functions,
570 | metadata=metadata["debug"],
571 | )
572 | response_end_time = time.time()
573 | footer_items = []
574 |
575 | # Add the token usage message to the footer items
576 | if completion_response:
577 | response_tokens = completion_response.usage.completion_tokens if completion_response.usage else 0
578 | request_tokens = token_budget.used
579 | footer_items.append(
580 | get_token_usage_message(
581 | max_tokens=config.request_config.max_tokens,
582 | total_tokens=request_tokens + response_tokens,
583 | request_tokens=request_tokens,
584 | completion_tokens=response_tokens,
585 | )
586 | )
587 |
588 | await context.update_conversation(
589 | metadata={
590 | "token_counts": {
591 | "total": request_tokens + response_tokens,
592 | "max": config.request_config.max_tokens,
593 | }
594 | }
595 | )
596 |
597 | footer_items.append(get_response_duration_message(response_end_time - response_start_time))
598 | metadata["footer_items"] = footer_items
599 |
600 | content = message_content_from_completion(completion_response)
601 | if not content:
602 | content = "I've processed your request, but couldn't generate a proper response."
603 |
604 | except Exception as e:
605 | logger.exception(f"exception occurred calling openai chat completion: {e}")
606 | content = "An error occurred while calling the OpenAI API. Is it configured correctly?"
607 | metadata["debug"]["error"] = str(e)
608 |
609 | if content:
610 | # strip out the username from the response
611 | if isinstance(content, str) and content.startswith("["):
612 | content = re.sub(r"\[.*\]:\s", "", content)
613 |
614 | # check for the silence token, in case the model chooses not to respond
615 | # model sometimes puts extra spaces in the response, so remove them
616 | # when checking for the silence token
617 | if isinstance(content, str) and content.replace(" ", "") == SILENCE_TOKEN:
618 | # normal behavior is to not respond if the model chooses to remain silent
619 | # but we can override this behavior for debugging purposes via the assistant config
620 | if config.enable_debug_output:
621 | metadata["debug"]["silence_token"] = True
622 | metadata["debug"]["silence_token_response"] = (content,)
623 | await context.send_messages(
624 | NewConversationMessage(
625 | message_type=MessageType.notice,
626 | content="[assistant chose to remain silent]",
627 | metadata=metadata,
628 | )
629 | )
630 | return
631 |
632 | # Prepare response.
633 | response_parts: list[str] = []
634 | try:
635 | if role == ConversationRole.TEAM:
636 | output_model = TeamOutput.model_validate_json(content)
637 | if output_model.response:
638 | response_parts.append(output_model.response)
639 |
640 | if output_model.excerpt:
641 | output_model.excerpt = output_model.excerpt.strip().strip('"')
642 | response_parts.append(f'> _"{output_model.excerpt}"_ (excerpt)')
643 |
644 | if output_model.citations:
645 | citations = ", ".join(output_model.citations)
646 | response_parts.append(f"Sources: _{citations}_")
647 |
648 | if output_model.next_step_suggestion:
649 | metadata["help"] = output_model.next_step_suggestion
650 |
651 | else:
652 | output_model = CoordinatorOutput.model_validate_json(content)
653 | if output_model.response:
654 | response_parts.append(output_model.response)
655 | if output_model.next_step_suggestion:
656 | metadata["help"] = output_model.next_step_suggestion
657 |
658 | except Exception as e:
659 | logger.exception(f"exception occurred parsing json response: {e}")
660 | metadata["debug"]["error"] = str(e)
661 | response_parts.append(content)
662 |
663 | await context.send_messages(
664 | NewConversationMessage(
665 | content="\n\n".join(response_parts),
666 | message_type=MessageType.chat,
667 | metadata=metadata,
668 | )
669 | )
670 |
671 |
672 | def get_formatted_token_count(tokens: int) -> str:
673 | # if less than 1k, return the number of tokens
674 | # if greater than or equal to 1k, return the number of tokens in k
675 | # use 1 decimal place for k
676 | # drop the decimal place if the number of tokens in k is a whole number
677 | if tokens < 1000:
678 | return str(tokens)
679 | else:
680 | tokens_in_k = tokens / 1000
681 | if tokens_in_k.is_integer():
682 | return f"{int(tokens_in_k)}k"
683 | else:
684 | return f"{tokens_in_k:.1f}k"
685 |
686 |
687 | def get_token_usage_message(
688 | max_tokens: int,
689 | total_tokens: int,
690 | request_tokens: int,
691 | completion_tokens: int,
692 | ) -> str:
693 | """
694 | Generate a display friendly message for the token usage, to be added to the footer items.
695 | """
696 |
697 | return dedent(f"""
698 | Tokens used: {get_formatted_token_count(total_tokens)}
699 | ({get_formatted_token_count(request_tokens)} in / {get_formatted_token_count(completion_tokens)} out)
700 | of {get_formatted_token_count(max_tokens)} ({int(total_tokens / max_tokens * 100)}%)
701 | """).strip()
702 |
703 |
704 | def get_response_duration_message(response_duration: float) -> str:
705 | """
706 | Generate a display friendly message for the response duration, to be added to the footer items.
707 | """
708 |
709 | return f"Response time: {response_duration:.2f} seconds"
710 |
```
--------------------------------------------------------------------------------
/assistants/document-assistant/assistant/response/responder.py:
--------------------------------------------------------------------------------
```python
1 | # Copyright (c) Microsoft. All rights reserved.
2 |
3 | import asyncio
4 | import json
5 | import logging
6 | import time
7 | from contextlib import AsyncExitStack
8 | from typing import Any, Awaitable, Callable
9 |
10 | import deepmerge
11 | import pendulum
12 | from assistant_extensions.attachments import get_attachments
13 | from assistant_extensions.chat_context_toolkit.message_history import (
14 | chat_context_toolkit_message_provider_for,
15 | construct_attachment_summarizer,
16 | )
17 | from assistant_extensions.chat_context_toolkit.virtual_filesystem import (
18 | archive_file_source_mount,
19 | )
20 | from assistant_extensions.mcp import (
21 | ExtendedCallToolRequestParams,
22 | MCPClientSettings,
23 | MCPServerConnectionError,
24 | OpenAISamplingHandler,
25 | establish_mcp_sessions,
26 | get_enabled_mcp_server_configs,
27 | get_mcp_server_prompts,
28 | list_roots_callback_for,
29 | refresh_mcp_sessions,
30 | sampling_message_to_chat_completion_message,
31 | )
32 | from chat_context_toolkit.history import NewTurn, apply_budget_to_history_messages
33 | from chat_context_toolkit.virtual_filesystem import FileEntry, VirtualFileSystem
34 | from liquid import render
35 | from mcp import SamplingMessage, ServerNotification
36 | from mcp.types import (
37 | TextContent,
38 | )
39 | from openai.types.chat import (
40 | ChatCompletionMessageParam,
41 | ChatCompletionSystemMessageParam,
42 | ChatCompletionToolParam,
43 | )
44 | from openai_client import (
45 | create_client,
46 | num_tokens_from_message,
47 | num_tokens_from_messages,
48 | num_tokens_from_tools,
49 | )
50 | from pydantic import BaseModel
51 | from semantic_workbench_api_model import workbench_model
52 | from semantic_workbench_api_model.workbench_model import (
53 | ConversationMessage,
54 | MessageType,
55 | NewConversationMessage,
56 | UpdateParticipant,
57 | )
58 | from semantic_workbench_assistant.assistant_app import (
59 | ConversationContext,
60 | )
61 |
62 | from assistant.config import AssistantConfigModel
63 | from assistant.context_management.inspector import ContextManagementInspector
64 | from assistant.filesystem import (
65 | EDIT_TOOL_DESCRIPTION_HOSTED,
66 | EDIT_TOOL_DESCRIPTION_LOCAL,
67 | VIEW_TOOL_OBJ,
68 | AttachmentsExtension,
69 | )
70 | from assistant.filesystem._file_sources import attachments_file_source_mount, editable_documents_file_source_mount
71 | from assistant.filesystem._filesystem import _files_drive_for_context
72 | from assistant.filesystem._prompts import ARCHIVES_ADDON_PROMPT, FILES_PROMPT, LS_TOOL_OBJ
73 | from assistant.guidance.dynamic_ui_inspector import get_dynamic_ui_state, update_dynamic_ui_state
74 | from assistant.guidance.guidance_prompts import DYNAMIC_UI_TOOL_NAME, DYNAMIC_UI_TOOL_OBJ
75 | from assistant.response.completion_handler import handle_completion
76 | from assistant.response.models import StepResult
77 | from assistant.response.prompts import ORCHESTRATION_SYSTEM_PROMPT, tool_abbreviations
78 | from assistant.response.utils import get_ai_client_configs, get_completion, get_openai_tools_from_mcp_sessions
79 | from assistant.response.utils.tokens_tiktoken import TokenizerOpenAI
80 | from assistant.whiteboard import notify_whiteboard
81 |
82 | logger = logging.getLogger(__name__)
83 |
84 |
85 | class ConversationMessageMetadata(BaseModel):
86 | associated_filenames: str | None = None
87 |
88 |
89 | # region Initialization
90 |
91 |
92 | class ConversationResponder:
93 | def __init__(
94 | self,
95 | message: ConversationMessage,
96 | context: ConversationContext,
97 | config: AssistantConfigModel,
98 | metadata: dict[str, Any],
99 | attachments_extension: AttachmentsExtension,
100 | context_management_inspector: ContextManagementInspector,
101 | ) -> None:
102 | self.message = message
103 | self.context = context
104 | self.config = config
105 | self.metadata = metadata
106 | self.attachments_extension = attachments_extension
107 | self.context_management_inspector = context_management_inspector
108 | self.latest_telemetry = context_management_inspector.get_telemetry(context.id)
109 |
110 | self.stack = AsyncExitStack()
111 |
112 | # Constants
113 | self.token_model = "gpt-4o"
114 | # The maximum number of tokens that each sub-component of the system prompt can have.
115 | self.max_system_prompt_component_tokens = 2000
116 | # Max number of tokens that should go into a request
117 | max_total_tokens_from_config = self.config.orchestration.prompts.max_total_tokens
118 | self.max_total_tokens = (
119 | int(self.config.generative_ai_client_config.request_config.max_tokens * 0.95)
120 | if max_total_tokens_from_config == -1
121 | else max_total_tokens_from_config
122 | )
123 |
124 | token_window_from_config = self.config.orchestration.prompts.token_window
125 | self.token_window = (
126 | int(self.max_total_tokens * 0.2) if token_window_from_config == -1 else token_window_from_config
127 | )
128 |
129 | self.tokenizer = TokenizerOpenAI(model=self.token_model)
130 |
131 | # Chat Context Toolkit
132 | self.history_turn = NewTurn()
133 | self.virtual_filesystem = VirtualFileSystem(
134 | mounts=[
135 | archive_file_source_mount(context),
136 | attachments_file_source_mount(context, attachments_extension),
137 | editable_documents_file_source_mount(context, _files_drive_for_context),
138 | ],
139 | )
140 |
141 | @classmethod
142 | async def create(
143 | cls,
144 | message: ConversationMessage,
145 | context: ConversationContext,
146 | config: AssistantConfigModel,
147 | metadata: dict[str, Any],
148 | attachments_extension: AttachmentsExtension,
149 | context_management_inspector: ContextManagementInspector,
150 | ) -> "ConversationResponder":
151 | responder = cls(message, context, config, metadata, attachments_extension, context_management_inspector)
152 | await responder._setup()
153 | return responder
154 |
155 | async def _setup(self) -> None:
156 | await self._setup_mcp()
157 |
158 | # endregion
159 |
160 | # region Responding Loop
161 |
162 | async def respond_to_conversation(self) -> None:
163 | interrupted = False
164 | encountered_error = False
165 | completed_within_max_steps = False
166 | step_count = 0
167 | while step_count < self.config.orchestration.options.max_steps:
168 | step_count += 1
169 | self.mcp_sessions = await refresh_mcp_sessions(self.mcp_sessions, self.stack)
170 |
171 | # Check to see if we should interrupt our flow
172 | last_message = await self.context.get_messages(limit=1, message_types=[MessageType.chat])
173 | if step_count > 1 and last_message.messages[0].sender.participant_id != self.context.assistant.id:
174 | # The last message was from a sender other than the assistant, so we should
175 | # interrupt our flow as this would have kicked off a new response from this
176 | # assistant with the new message in mind and that process can decide if it
177 | # should continue with the current flow or not.
178 | interrupted = True
179 | logger.info("Response interrupted by user message.")
180 | break
181 |
182 | step_result = await self._step(step_count)
183 |
184 | match step_result.status:
185 | case "final":
186 | completed_within_max_steps = True
187 | break
188 | case "error":
189 | encountered_error = True
190 | break
191 |
192 | # If the response did not complete within the maximum number of steps, send a message to the user
193 | if not completed_within_max_steps and not encountered_error and not interrupted:
194 | await self.context.send_messages(
195 | NewConversationMessage(
196 | content=self.config.orchestration.options.max_steps_truncation_message,
197 | message_type=MessageType.notice,
198 | metadata=self.metadata,
199 | )
200 | )
201 | logger.info("Response stopped early due to maximum steps.")
202 |
203 | await self._cleanup()
204 |
205 | # endregion
206 |
207 | # region Response Step
208 |
209 | async def _step(self, step_count) -> StepResult:
210 | step_result = StepResult(status="continue", metadata=self.metadata.copy())
211 |
212 | response_start_time = time.time()
213 |
214 | tools, chat_message_params = await self._construct_prompt()
215 |
216 | await self.context_management_inspector.update_state(self.context)
217 |
218 | self.sampling_handler.message_processor = await self._update_sampling_message_processor(
219 | chat_history=chat_message_params
220 | )
221 |
222 | await notify_whiteboard(
223 | context=self.context,
224 | server_config=self.config.orchestration.hosted_mcp_servers.memory_whiteboard,
225 | attachment_messages=[],
226 | chat_messages=chat_message_params[1:],
227 | )
228 |
229 | async with create_client(self.config.generative_ai_client_config.service_config) as client:
230 | async with self.context.set_status("thinking..."):
231 | try:
232 | # If user guidance is enabled, we transparently run two LLM calls with very similar parameters.
233 | # One is the mainline LLM call for the orchestration, the other is identically expect it forces the LLM to
234 | # call the DYNAMIC_UI_TOOL_NAME function to generate UI elements right after a user message is sent (the first step).
235 | # This is done to only interrupt the user letting them know when the LLM deems it to be necessary.
236 | # Otherwise, UI elements are generated in the background.
237 | # Finally, we use the same parameters for both calls so that LLM understands the capabilities of the assistant when generating UI elements.
238 | deepmerge.always_merger.merge(
239 | self.metadata,
240 | {
241 | "debug": {
242 | f"respond_to_conversation:step_{step_count}": {
243 | "request": {
244 | "model": self.config.generative_ai_client_config.request_config.model,
245 | "messages": chat_message_params,
246 | "max_tokens": self.config.generative_ai_client_config.request_config.response_tokens,
247 | "tools": tools,
248 | },
249 | },
250 | },
251 | },
252 | )
253 | completion_dynamic_ui = None
254 | if self.config.orchestration.guidance.enabled and step_count == 1:
255 | dynamic_ui_task = get_completion(
256 | client,
257 | self.config.generative_ai_client_config.request_config,
258 | chat_message_params,
259 | tools,
260 | tool_choice=DYNAMIC_UI_TOOL_NAME,
261 | )
262 | completion_task = get_completion(
263 | client, self.config.generative_ai_client_config.request_config, chat_message_params, tools
264 | )
265 | completion_dynamic_ui, completion = await asyncio.gather(dynamic_ui_task, completion_task)
266 | else:
267 | completion = await get_completion(
268 | client, self.config.generative_ai_client_config.request_config, chat_message_params, tools
269 | )
270 |
271 | except Exception as e:
272 | logger.exception(f"exception occurred calling openai chat completion: {e}")
273 | deepmerge.always_merger.merge(
274 | step_result.metadata,
275 | {
276 | "debug": {
277 | f"respond_to_conversation:step_{step_count}": {
278 | "error": str(e),
279 | },
280 | },
281 | },
282 | )
283 | await self.context.send_messages(
284 | NewConversationMessage(
285 | content="An error occurred while calling the OpenAI API. Is it configured correctly?"
286 | " View the debug inspector for more information.",
287 | message_type=MessageType.notice,
288 | metadata=step_result.metadata,
289 | )
290 | )
291 | step_result.status = "error"
292 | return step_result
293 |
294 | if self.config.orchestration.guidance.enabled and completion_dynamic_ui:
295 | # Check if the regular request generated the DYNAMIC_UI_TOOL_NAME
296 | called_dynamic_ui_tool = False
297 | if completion.choices[0].message.tool_calls:
298 | for tool_call in completion.choices[0].message.tool_calls:
299 | if tool_call.function.name == DYNAMIC_UI_TOOL_NAME:
300 | called_dynamic_ui_tool = True
301 | # Open the dynamic UI inspector tab
302 | await self.context.send_conversation_state_event(
303 | workbench_model.AssistantStateEvent(
304 | state_id="dynamic_ui",
305 | event="focus",
306 | state=None,
307 | )
308 | )
309 |
310 | # If it did, completely ignore the special completion. Otherwise, use it to generate UI for this turn
311 | if not called_dynamic_ui_tool:
312 | tool_calls = completion_dynamic_ui.choices[0].message.tool_calls
313 | # Otherwise, use it generate the UI for this return
314 | if tool_calls:
315 | tool_call = tool_calls[0]
316 | tool_call = ExtendedCallToolRequestParams(
317 | id=tool_call.id,
318 | name=tool_call.function.name,
319 | arguments=json.loads(
320 | tool_call.function.arguments,
321 | ),
322 | ) # Check if any ui_elements were generated and abort early if not
323 | if tool_call.arguments and tool_call.arguments.get("ui_elements", []):
324 | await update_dynamic_ui_state(self.context, tool_call.arguments)
325 |
326 | step_result = await handle_completion(
327 | step_result,
328 | completion,
329 | self.mcp_sessions,
330 | self.context,
331 | self.config.generative_ai_client_config.request_config,
332 | f"respond_to_conversation:step_{step_count}",
333 | response_start_time,
334 | self.config.orchestration.guidance.enabled,
335 | self.virtual_filesystem,
336 | )
337 | return step_result
338 |
339 | # endregion
340 |
341 | # region Prompt Construction
342 |
343 | async def _construct_prompt(self) -> tuple[list, list[ChatCompletionMessageParam]]:
344 | # Set tools
345 | tools = []
346 | if self.config.orchestration.guidance.enabled:
347 | tools.append(DYNAMIC_UI_TOOL_OBJ)
348 | tools.extend(
349 | get_openai_tools_from_mcp_sessions(self.mcp_sessions, self.config.orchestration.tools_disabled) or []
350 | )
351 | # Remove any view tool that was added by an MCP server and replace it with ours.
352 | # Also remove the list_working_directory tool because we will automatically inject available files into the system prompt.
353 | tools = [tool for tool in tools if tool["function"]["name"] not in ["view", "list_working_directory", "ls"]]
354 | tools.append(VIEW_TOOL_OBJ)
355 | tools.append(LS_TOOL_OBJ)
356 | # Override the description of the edit_file depending on the environment
357 | tools = self._override_edit_file_description(tools)
358 |
359 | # Note: Currently assuming system prompt will fit into the token budget.
360 | # Start constructing main system prompt
361 | # Inject the {{knowledge_cutoff}} and {{current_date}} placeholders
362 | main_system_prompt = render(
363 | ORCHESTRATION_SYSTEM_PROMPT,
364 | **{
365 | "knowledge_cutoff": self.config.orchestration.prompts.knowledge_cutoff,
366 | "current_date": pendulum.now(tz="America/Los_Angeles").format("YYYY-MM-DD"),
367 | },
368 | )
369 |
370 | # Construct key parts of the system messages which are core capabilities.
371 | # Best practice is to have these start with a ## <heading content>
372 | # User Guidance and & Dynamic UI Generation
373 | if self.config.orchestration.guidance.enabled:
374 | dynamic_ui_system_prompt = self.tokenizer.truncate_str(
375 | await self._construct_dynamic_ui_system_prompt(),
376 | 10000, # For now, don't limit this that much
377 | )
378 | main_system_prompt += "\n\n" + dynamic_ui_system_prompt.strip()
379 |
380 | # Filesystem System Prompt
381 | ls_result = await self._construct_filesystem_system_prompt()
382 | filesystem_system_prompt = self.tokenizer.truncate_str(ls_result, max_len=10000)
383 | main_system_prompt += "\n\n" + filesystem_system_prompt.strip()
384 |
385 | # Add specific guidance from MCP servers
386 | mcp_prompts = await get_mcp_server_prompts(self.mcp_sessions)
387 | mcp_prompt_string = self.tokenizer.truncate_str(
388 | "## MCP Servers" + "\n\n" + "\n\n".join(mcp_prompts), self.max_system_prompt_component_tokens
389 | )
390 | main_system_prompt += "\n\n" + mcp_prompt_string.strip()
391 |
392 | # Always append the guardrails postfix at the end.
393 | main_system_prompt += "\n\n" + self.config.orchestration.prompts.guardrails_prompt.strip()
394 | self.latest_telemetry.system_prompt = main_system_prompt
395 |
396 | main_system_prompt = ChatCompletionSystemMessageParam(
397 | role="system",
398 | content=main_system_prompt,
399 | )
400 |
401 | message_provider = chat_context_toolkit_message_provider_for(
402 | context=self.context,
403 | tool_abbreviations=tool_abbreviations,
404 | attachments=list(
405 | await get_attachments(
406 | self.context,
407 | summarizer=construct_attachment_summarizer(
408 | service_config=self.config.generative_ai_fast_client_config.service_config,
409 | request_config=self.config.generative_ai_fast_client_config.request_config,
410 | ),
411 | )
412 | ),
413 | )
414 | system_prompt_token_count = num_tokens_from_message(main_system_prompt, model="gpt-4o")
415 | tool_token_count = num_tokens_from_tools(tools, model="gpt-4o")
416 | message_history_token_budget = (
417 | self.config.orchestration.prompts.max_total_tokens
418 | - system_prompt_token_count
419 | - tool_token_count
420 | - self.config.generative_ai_client_config.request_config.response_tokens
421 | )
422 | budgeted_messages_result = await apply_budget_to_history_messages(
423 | turn=self.history_turn,
424 | token_budget=message_history_token_budget,
425 | token_counter=lambda messages: num_tokens_from_messages(messages=messages, model="gpt-4o"),
426 | message_provider=message_provider,
427 | )
428 | chat_history: list[ChatCompletionMessageParam] = list(budgeted_messages_result.messages)
429 | chat_history.insert(0, main_system_prompt)
430 |
431 | logger.info("The system prompt has been constructed.")
432 | # Update telemetry for inspector
433 | self.latest_telemetry.system_prompt_tokens = system_prompt_token_count
434 | self.latest_telemetry.tool_tokens = tool_token_count
435 | self.latest_telemetry.message_tokens = num_tokens_from_messages(messages=chat_history, model="gpt-4o")
436 | self.latest_telemetry.total_context_tokens = (
437 | system_prompt_token_count + tool_token_count + self.latest_telemetry.message_tokens
438 | )
439 | self.latest_telemetry.final_messages = chat_history
440 |
441 | return tools, chat_history
442 |
443 | async def _construct_dynamic_ui_system_prompt(self) -> str:
444 | current_dynamic_ui_elements = await get_dynamic_ui_state(context=self.context)
445 |
446 | if not current_dynamic_ui_elements:
447 | current_dynamic_ui_elements = "No dynamic UI elements have been generated yet. Consider generating some."
448 |
449 | system_prompt = "## On Dynamic UI Elements\n"
450 | system_prompt += "\n" + self.config.orchestration.guidance.prompt
451 | system_prompt += "\n" + str(current_dynamic_ui_elements)
452 | return system_prompt
453 |
454 | async def _construct_filesystem_system_prompt(self) -> str:
455 | """Constructs the filesystem system prompt with available files.
456 |
457 | Builds a system prompt that includes:
458 | 1. FILES_PROMPT with attachments and editable_documents (up to 25 files)
459 | 2. ARCHIVES_ADDON_PROMPT (if archives exist)
460 | 3. Archives files listing (up to 25 files)
461 |
462 | Files are sorted by timestamp (newest first), limited to 25 per category,
463 | then sorted alphabetically by path.
464 |
465 | This is an example of what gets added after the FILES_PROMPT:
466 | -r-- path2.pdf [File content summary: <summary>]
467 | -rw- path3.txt [File content summary: No summary available yet, use the context available to determine the use of this file]
468 | """
469 | # Get all file entries
470 | attachments_entries = list(await self.virtual_filesystem.list_directory(path="/attachments"))
471 | editable_documents_entries = list(await self.virtual_filesystem.list_directory(path="/editable_documents"))
472 | archives_entries = list(await self.virtual_filesystem.list_directory(path="/archives"))
473 |
474 | # Separate regular files from archives
475 | regular_files = [
476 | entry for entry in (attachments_entries + editable_documents_entries) if isinstance(entry, FileEntry)
477 | ]
478 | archives_files = [entry for entry in archives_entries if isinstance(entry, FileEntry)]
479 |
480 | # TODO: Better ranking algorithm
481 | # order the regular files by timestamp, newest first
482 | regular_files.sort(
483 | key=lambda f: f.timestamp.timestamp() if hasattr(f.timestamp, "timestamp") else f.timestamp, reverse=True
484 | )
485 | # take the top 25 regular files
486 | regular_files = regular_files[:25]
487 | # order them alphabetically by path
488 | regular_files.sort(key=lambda f: f.path.lower())
489 |
490 | # Start with FILES_PROMPT and add attachments/editable_documents
491 | system_prompt = FILES_PROMPT + "\n"
492 | if not regular_files:
493 | system_prompt += "\nNo files are currently available."
494 |
495 | for file in regular_files:
496 | # Format permissions: -rw- for read_write, -r-- for read
497 | permissions = "-rw-" if file.permission == "read_write" else "-r--"
498 | # Use the file description as the summary, or provide a default message
499 | summary = (
500 | file.description
501 | if file.description
502 | else "No summary available yet, use the context available to determine the use of this file"
503 | )
504 | system_prompt += f"{permissions} {file.path} [File content summary: {summary}]\n"
505 |
506 | # Add ARCHIVES_ADDON_PROMPT if there are archives
507 | if archives_files:
508 | system_prompt += "\n" + ARCHIVES_ADDON_PROMPT + "\n"
509 |
510 | # order the archives files by timestamp, newest first
511 | archives_files.sort(
512 | key=lambda f: f.timestamp.timestamp() if hasattr(f.timestamp, "timestamp") else f.timestamp,
513 | reverse=True,
514 | )
515 | # take the top 25 archives files
516 | archives_files = archives_files[:25]
517 | # order them alphabetically by path
518 | archives_files.sort(key=lambda f: f.path.lower())
519 |
520 | for file in archives_files:
521 | # Format permissions: -rw- for read_write, -r-- for read
522 | permissions = "-rw-" if file.permission == "read_write" else "-r--"
523 | # Use the file description as the summary, or provide a default message
524 | summary = (
525 | file.description
526 | if file.description
527 | else "No summary available yet, use the context available to determine the use of this file"
528 | )
529 | system_prompt += f"{permissions} {file.path} [File content summary: {summary}]\n"
530 |
531 | return system_prompt
532 |
533 | def _override_edit_file_description(self, tools: list[ChatCompletionToolParam]) -> list[ChatCompletionToolParam]:
534 | """
535 | Override the edit_file description based on the root (the one that indicates the hosted env, otherwise assume the local env).
536 | """
537 | try:
538 | # Get the root of the filesystem-edit tool
539 | # Find the filesystem MCP by name
540 | filesystem_mcp = next(
541 | (mcp for mcp in self.mcp_sessions if mcp.config.server_config.key == "filesystem-edit"),
542 | None,
543 | )
544 | filesystem_root = None
545 | if filesystem_mcp:
546 | # Get the root of the filesystem-edit tool
547 | filesystem_root = next(
548 | (root for root in filesystem_mcp.config.server_config.roots if root.name == "root"),
549 | None,
550 | )
551 |
552 | edit_tool = next(
553 | (tool for tool in tools if tool["function"]["name"] == "edit_file"),
554 | None,
555 | )
556 | if filesystem_root and filesystem_root.uri == "file://workspace" and edit_tool:
557 | edit_tool["function"]["description"] = EDIT_TOOL_DESCRIPTION_HOSTED
558 | elif filesystem_root and edit_tool:
559 | edit_tool["function"]["description"] = EDIT_TOOL_DESCRIPTION_LOCAL
560 | except Exception:
561 | logger.exception("Failed to override edit_file description")
562 | return tools
563 |
564 | return tools
565 |
566 | # endregion
567 |
568 | # region MCP Sessions
569 |
570 | async def _update_sampling_message_processor(
571 | self,
572 | chat_history: list[ChatCompletionMessageParam],
573 | ) -> Callable[[list[SamplingMessage], int, str], Awaitable[list[ChatCompletionMessageParam]]]:
574 | """
575 | Constructs function that will inject context from the assistant into sampling calls from the MCP server if it requests it.
576 | Currently supports a custom message of:
577 | `{"variable": "history_messages"}` which will inject the chat history with attachments into the sampling call.
578 | """
579 |
580 | async def _sampling_message_processor(
581 | messages: list[SamplingMessage], available_tokens: int, model: str
582 | ) -> list[ChatCompletionMessageParam]:
583 | updated_messages: list[ChatCompletionMessageParam] = []
584 |
585 | for message in messages:
586 | if not isinstance(message.content, TextContent):
587 | updated_messages.append(sampling_message_to_chat_completion_message(message))
588 | continue
589 |
590 | # Determine if the message.content.text is a json payload
591 | content = message.content.text
592 | if not content.startswith("{") or not content.endswith("}"):
593 | updated_messages.append(sampling_message_to_chat_completion_message(message))
594 | continue
595 |
596 | # Attempt to parse the json payload
597 | try:
598 | json_payload = json.loads(content)
599 | variable = json_payload.get("variable")
600 | match variable:
601 | case "attachment_messages":
602 | # Ignore this for now, as we are handling attachments in the main message
603 | continue
604 | case "history_messages":
605 | # Always skip the first message in the chat history, as it is the system prompt
606 | if len(chat_history) > 1:
607 | updated_messages.extend(chat_history[1:])
608 | continue
609 | case _:
610 | updated_messages.append(sampling_message_to_chat_completion_message(message))
611 | continue
612 | except json.JSONDecodeError:
613 | updated_messages.append(sampling_message_to_chat_completion_message(message))
614 | continue
615 |
616 | return updated_messages
617 |
618 | return _sampling_message_processor
619 |
620 | async def _setup_mcp(self) -> None:
621 | generative_ai_client_config = get_ai_client_configs(self.config, "generative")
622 | reasoning_ai_client_config = get_ai_client_configs(self.config, "reasoning")
623 |
624 | sampling_handler = OpenAISamplingHandler(
625 | ai_client_configs=[
626 | generative_ai_client_config,
627 | reasoning_ai_client_config,
628 | ],
629 | )
630 | self.sampling_handler = sampling_handler
631 |
632 | async def message_handler(message) -> None:
633 | if isinstance(message, ServerNotification) and message.root.method == "notifications/message":
634 | await self.context.update_participant_me(UpdateParticipant(status=f"{message.root.params.data}"))
635 |
636 | client_resource_handler = self.attachments_extension.client_resource_handler_for(self.context)
637 |
638 | enabled_servers = get_enabled_mcp_server_configs(self.config.orchestration.mcp_servers)
639 |
640 | try:
641 | mcp_sessions = await establish_mcp_sessions(
642 | client_settings=[
643 | MCPClientSettings(
644 | server_config=server_config,
645 | sampling_callback=self.sampling_handler.handle_message,
646 | message_handler=message_handler,
647 | list_roots_callback=list_roots_callback_for(context=self.context, server_config=server_config),
648 | experimental_resource_callbacks=(
649 | client_resource_handler.handle_list_resources,
650 | client_resource_handler.handle_read_resource,
651 | client_resource_handler.handle_write_resource,
652 | ),
653 | )
654 | for server_config in enabled_servers
655 | ],
656 | stack=self.stack,
657 | )
658 | self.mcp_sessions = mcp_sessions
659 | except MCPServerConnectionError as e:
660 | await self.context.send_messages(
661 | NewConversationMessage(
662 | content=f"Failed to connect to MCP server {e.server_config.key}: {e}",
663 | message_type=MessageType.notice,
664 | metadata=self.metadata,
665 | )
666 | )
667 |
668 | # endregion
669 |
670 | # region Misc
671 |
672 | async def _cleanup(self) -> None:
673 | await self.stack.aclose()
674 |
675 | # endregion
676 |
```