This is page 93 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
--------------------------------------------------------------------------------
/libraries/python/assistant-data-gen/assistant_data_gen/gce/gce_agent.py:
--------------------------------------------------------------------------------
```python
1 | # Copyright (c) Microsoft. All rights reserved.
2 |
3 | from enum import Enum
4 | from typing import Literal, Sequence
5 |
6 | from chat_context_toolkit.history import (
7 | HistoryMessage,
8 | HistoryMessageProtocol,
9 | HistoryMessageProvider,
10 | NewTurn,
11 | OpenAIHistoryMessageParam,
12 | apply_budget_to_history_messages,
13 | )
14 | from chat_context_toolkit.history.tool_abbreviations import (
15 | Abbreviations,
16 | ToolAbbreviations,
17 | abbreviate_openai_tool_message,
18 | )
19 | from liquid import render
20 | from openai_client import (
21 | num_tokens_from_messages,
22 | )
23 | from pydantic import BaseModel, Field
24 | from pydantic_ai import Agent, ModelRetry, RunContext, UnexpectedModelBehavior
25 | from pydantic_ai.direct import model_request
26 | from pydantic_ai.messages import (
27 | ModelMessage,
28 | ModelRequest,
29 | ModelRequestPart,
30 | ModelResponse,
31 | ModelResponsePart,
32 | SystemPromptPart,
33 | TextPart,
34 | ToolCallPart,
35 | ToolReturnPart,
36 | UserPromptPart,
37 | )
38 | from pydantic_ai.models import ModelRequestParameters
39 | from pydantic_ai.models.openai import OpenAIModel
40 | from pydantic_ai.providers.openai import OpenAIProvider
41 | from pydantic_ai.tools import ToolDefinition
42 |
43 | from assistant_data_gen.gce.prompts import (
44 | AGENDA_SYSTEM_PROMPT,
45 | CONVERSATION_SYSTEM_PROMPT,
46 | FIRST_USER_MESSAGE,
47 | RESOURCE_INSTRUCTIONS_EXACT,
48 | RESOURCE_INSTRUCTIONS_MAXIMUM,
49 | TERMINATION_INSTRUCTIONS_EXACT,
50 | TERMINATION_INSTRUCTIONS_MAXIMUM,
51 | )
52 | from assistant_data_gen.pydantic_ai_utils import create_model
53 |
54 |
55 | class ResourceConstraintMode(Enum):
56 | """Choose how the agent should use the resource.
57 | Maximum: is an upper bound, i.e. the agent can end the conversation before the resource is exhausted
58 | Exact: the agent should aim to use exactly the given amount of the resource"""
59 |
60 | MAXIMUM = "maximum"
61 | EXACT = "exact"
62 |
63 | @staticmethod
64 | def get_termination_instructions(mode: "ResourceConstraintMode") -> str:
65 | """Get termination instructions based on the resource constraint mode."""
66 | if mode == ResourceConstraintMode.MAXIMUM:
67 | return TERMINATION_INSTRUCTIONS_MAXIMUM
68 | elif mode == ResourceConstraintMode.EXACT:
69 | return TERMINATION_INSTRUCTIONS_EXACT
70 |
71 | @staticmethod
72 | def get_resource_instructions(mode: "ResourceConstraintMode") -> str:
73 | """Get resource instructions based on the resource constraint mode."""
74 | if mode == ResourceConstraintMode.MAXIMUM:
75 | return RESOURCE_INSTRUCTIONS_MAXIMUM
76 | elif mode == ResourceConstraintMode.EXACT:
77 | return RESOURCE_INSTRUCTIONS_EXACT
78 |
79 |
80 | class AgendaItem(BaseModel):
81 | resource: int = Field(description="Number of turns required for the item")
82 | description: str = Field(
83 | description="Detailed description of what to discuss for this agenda item over the number of turns"
84 | )
85 |
86 |
87 | class Agenda(BaseModel):
88 | items: list[AgendaItem] = Field(
89 | description="Ordered list of items to be completed in the remainder of the conversation",
90 | default_factory=list,
91 | )
92 |
93 | def format_for_llm(self) -> str:
94 | if not self.items:
95 | return "No agenda items currently planned."
96 | formatted_items = []
97 | for i, item in enumerate(self.items, 1):
98 | if i == 1:
99 | formatted_items.append(
100 | f"[{i} (Current item you must accomplish in the provided number of turns)] {item.description} ({item.resource} turns)"
101 | )
102 | else:
103 | formatted_items.append(f"[{i}] {item.description} ({item.resource} turns)")
104 | return "Current agenda:\n" + "\n".join(formatted_items)
105 |
106 |
107 | class GuidedConversationInput(BaseModel):
108 | context: str
109 | rules: list[str]
110 | conversation_flow: str
111 | resource_constraint_mode: ResourceConstraintMode
112 | provider: Literal["openai", "anthropic", "azure_openai"]
113 |
114 |
115 | class GuidedConversationState(BaseModel):
116 | agenda: Agenda
117 | message_history: list[ModelMessage]
118 | resource_total: int
119 | resource_elapsed: int = 0
120 | conversation_ended: bool = False
121 | last_assistant_message: str | None = None
122 |
123 |
124 | # region Message History Management Setup
125 |
126 |
127 | def convert_openai_to_pydantic_ai(openai_messages: Sequence[OpenAIHistoryMessageParam]) -> list[ModelMessage]:
128 | """Convert OpenAI message format back to Pydantic AI ModelMessage format.
129 |
130 | This reverses the conversion done by OpenAI's _map_messages method.
131 | """
132 | pydantic_messages: list[ModelMessage] = []
133 | # Keep track of tool call id to tool name mapping
134 | tool_call_id_to_name: dict[str, str] = {}
135 |
136 | for msg in openai_messages:
137 | role = msg["role"]
138 | if role == "user":
139 | # Convert user messages to ModelRequest with UserPromptPart
140 | content = msg.get("content", "")
141 | if isinstance(content, str):
142 | parts: list[ModelRequestPart] = [UserPromptPart(content=content)]
143 | else:
144 | # NOTE: For multi-modal content, convert to string for simplicity
145 | parts = [UserPromptPart(content=str(content))]
146 | pydantic_messages.append(ModelRequest(parts=parts))
147 | elif role == "assistant":
148 | # Convert assistant messages to ModelResponse
149 | response_parts: list[ModelResponsePart] = []
150 | # Handle text content
151 | if content := msg.get("content"):
152 | if isinstance(content, str):
153 | response_parts.append(TextPart(content=content))
154 | else:
155 | response_parts.append(TextPart(content=str(content)))
156 |
157 | # Handle tool calls
158 | if tool_calls := msg.get("tool_calls"):
159 | for tool_call in tool_calls:
160 | tool_name = tool_call["function"]["name"]
161 | tool_call_id = tool_call["id"]
162 | # Store mapping for later tool return messages
163 | tool_call_id_to_name[tool_call_id] = tool_name
164 | response_parts.append(
165 | ToolCallPart(
166 | tool_name=tool_name,
167 | args=tool_call["function"]["arguments"],
168 | tool_call_id=tool_call_id,
169 | )
170 | )
171 | if response_parts:
172 | pydantic_messages.append(ModelResponse(parts=response_parts))
173 |
174 | elif role == "tool":
175 | # Convert tool messages to ModelRequest with ToolReturnPart
176 | content = msg.get("content", "")
177 | tool_call_id = msg.get("tool_call_id", "")
178 | # Look up the tool name from our mapping
179 | tool_name = tool_call_id_to_name.get(tool_call_id, "unknown_tool")
180 | parts: list[ModelRequestPart] = [
181 | ToolReturnPart(
182 | tool_name=tool_name,
183 | content=content if isinstance(content, str) else str(content),
184 | tool_call_id=tool_call_id,
185 | )
186 | ]
187 | pydantic_messages.append(ModelRequest(parts=parts))
188 | return pydantic_messages
189 |
190 |
191 | class PydanticAIMessageWrapper(HistoryMessage):
192 | """Wrapper to make OpenAI message param compatible with MessageProtocol"""
193 |
194 | def __init__(self, openai_message_param: OpenAIHistoryMessageParam, message_id: str):
195 | super().__init__(id=message_id, openai_message=openai_message_param, abbreviator=self.abbreviator)
196 |
197 | def abbreviator(self) -> OpenAIHistoryMessageParam | None:
198 | tool_abbreviations = ToolAbbreviations({
199 | "update_agenda": Abbreviations(
200 | tool_call_argument_replacements={
201 | "items": "This agenda is old and has been removed due to token window limitations."
202 | }
203 | )
204 | })
205 |
206 | # For tool messages, we need to provide the tool name. This assumes the tool name is "update_agenda".
207 | tool_name_for_tool_message = None
208 | if self._openai_message.get("role") == "tool":
209 | # For tool messages, we need to determine the tool name
210 | # This should match the tool that was called - in our case "update_agenda"
211 | tool_name_for_tool_message = "update_agenda"
212 |
213 | prop = abbreviate_openai_tool_message(
214 | self._openai_message, tool_abbreviations, tool_name_for_tool_message=tool_name_for_tool_message
215 | )
216 | return prop
217 |
218 |
219 | def message_history_message_provider_for(messages: list[ModelMessage]) -> HistoryMessageProvider:
220 | async def provider() -> list[HistoryMessageProtocol]:
221 | # Convert all messages using Pydantic AI's built-in conversion
222 | _temp_model = OpenAIModel("gpt-4o", provider=OpenAIProvider(api_key="dummy-key"))
223 | openai_messages = await _temp_model._map_messages(messages)
224 |
225 | # Filter to only include message types supported by HistoryMessageParam
226 | # (user, assistant, tool messages - exclude system, developer, etc.)
227 | filtered_messages = []
228 | for msg in openai_messages:
229 | if msg.get("role") in ["user", "assistant", "tool"]:
230 | filtered_messages.append(msg) # type: ignore
231 |
232 | wrapped_messages = []
233 | # Find starting index if after_id is provided
234 | start_idx = 0
235 |
236 | for i, openai_msg in enumerate(filtered_messages[start_idx:], start_idx):
237 | msg_id = f"msg_{i}"
238 | wrapped_messages.append(PydanticAIMessageWrapper(openai_msg, msg_id))
239 | return wrapped_messages
240 |
241 | return provider
242 |
243 |
244 | async def process_message_history_with_budget(
245 | message_history: list[ModelMessage],
246 | token_budget: int = 32000,
247 | high_priority_token_count: int = 8000,
248 | ) -> list[ModelMessage]:
249 | message_provider = message_history_message_provider_for(message_history)
250 | budget_result = await apply_budget_to_history_messages(
251 | turn=NewTurn(high_priority_token_count=high_priority_token_count),
252 | token_budget=token_budget,
253 | token_counter=lambda messages: num_tokens_from_messages(messages=messages, model="gpt-4o"),
254 | message_provider=message_provider,
255 | )
256 | return convert_openai_to_pydantic_ai(budget_result.messages)
257 |
258 |
259 | # endregion
260 |
261 |
262 | async def step_conversation(
263 | gce_input: GuidedConversationInput,
264 | gce_state: GuidedConversationState,
265 | user_message: str | None,
266 | ) -> GuidedConversationState:
267 | # Use FIRST_USER_MESSAGE if user_message is None
268 | user_message = FIRST_USER_MESSAGE if user_message is None else user_message
269 | model = create_model(gce_input.provider)
270 | resource_remaining = gce_state.resource_total - gce_state.resource_elapsed
271 |
272 | # region Agenda Generation
273 | # First step: generate agenda updates with explicit prompt with retries.
274 | agenda_system_prompt = render(
275 | AGENDA_SYSTEM_PROMPT,
276 | **{
277 | "context": gce_input.context,
278 | "rules": "\n".join(gce_input.rules),
279 | "conversation_flow": gce_input.conversation_flow,
280 | "resource_remaining": resource_remaining,
281 | "resource_instructions": render(
282 | ResourceConstraintMode.get_resource_instructions(gce_input.resource_constraint_mode),
283 | **{"resource_remaining": resource_remaining},
284 | ),
285 | },
286 | )
287 | processed_messages = await process_message_history_with_budget(gce_state.message_history)
288 |
289 | agenda_agent = Agent[None, Agenda](
290 | model=model,
291 | instructions=agenda_system_prompt,
292 | output_type=Agenda,
293 | retries=5,
294 | )
295 |
296 | @agenda_agent.output_validator
297 | async def validate_agenda(_: RunContext[None], agenda: Agenda) -> Agenda:
298 | """Validate the generated agenda."""
299 | for item in agenda.items:
300 | if item.resource > 7:
301 | raise ModelRetry(
302 | f"Agenda item '{item.description}' requires {item.resource} turns, but items can have at most 7 turns"
303 | )
304 |
305 | total_resource = sum(item.resource for item in agenda.items)
306 | resource_remaining = gce_state.resource_total - gce_state.resource_elapsed
307 |
308 | if gce_input.resource_constraint_mode == ResourceConstraintMode.EXACT:
309 | if total_resource != resource_remaining:
310 | raise ModelRetry(
311 | f"For exact resource constraints, the sum of resources ({total_resource}) "
312 | f"must equal the remaining turns ({resource_remaining})"
313 | )
314 | else:
315 | if total_resource > resource_remaining:
316 | raise ModelRetry(
317 | f"The sum of resources ({total_resource}) exceeds the remaining turns ({resource_remaining})"
318 | )
319 |
320 | return agenda
321 |
322 | try:
323 | agenda_result = await agenda_agent.run(user_prompt=user_message, message_history=processed_messages)
324 | gce_state.agenda = agenda_result.output
325 | except UnexpectedModelBehavior as e:
326 | print(f"Unexpected model behavior: {e}\nContinuing with existing agenda.")
327 |
328 | # endregion
329 |
330 | # region Generate Assistant Response
331 |
332 | gce_state.message_history.append(ModelRequest(parts=[UserPromptPart(content=user_message)]))
333 | next_message_content = (
334 | gce_state.agenda.format_for_llm()
335 | + "\nNow I will generate a message that makes one step towards achieving the current agenda item."
336 | )
337 | new_agenda_message = ModelResponse(
338 | parts=[TextPart(content=next_message_content)],
339 | )
340 | gce_state.message_history.append(new_agenda_message)
341 | processed_messages = await process_message_history_with_budget(gce_state.message_history)
342 | system_prompt = render(
343 | CONVERSATION_SYSTEM_PROMPT,
344 | **{
345 | "context": gce_input.context,
346 | "rules": "\n".join(gce_input.rules),
347 | "conversation_flow": gce_input.conversation_flow,
348 | "resource_remaining": resource_remaining,
349 | "termination_instructions": ResourceConstraintMode.get_termination_instructions(
350 | gce_input.resource_constraint_mode
351 | ),
352 | "resource_instructions": render(
353 | ResourceConstraintMode.get_resource_instructions(gce_input.resource_constraint_mode),
354 | **{"resource_remaining": resource_remaining},
355 | ),
356 | },
357 | )
358 |
359 | processed_messages.insert(0, ModelRequest(parts=[SystemPromptPart(content=system_prompt)]))
360 | model_response: ModelResponse = await model_request(
361 | model=model,
362 | messages=processed_messages,
363 | model_request_parameters=ModelRequestParameters(
364 | function_tools=[
365 | ToolDefinition(
366 | name="end_conversation",
367 | description="End the conversation early. Only use when the conversation is truly stuck.",
368 | parameters_json_schema={},
369 | strict=False,
370 | )
371 | ],
372 | allow_text_output=True,
373 | ),
374 | )
375 | for part in model_response.parts:
376 | if isinstance(part, ToolCallPart) and part.tool_name == "end_conversation":
377 | gce_state.conversation_ended = True
378 | if isinstance(part, TextPart):
379 | gce_state.last_assistant_message = part.content
380 | gce_state.message_history.append(ModelResponse(parts=model_response.parts))
381 |
382 | gce_state.resource_elapsed += 1
383 | if gce_state.resource_elapsed >= gce_state.resource_total:
384 | gce_state.conversation_ended = True
385 | return gce_state
386 |
387 |
388 | # endregion
389 |
390 | # region Interactive Example Usage
391 |
392 | if __name__ == "__main__":
393 | import asyncio
394 |
395 | context = """You are working 1 on 1 with David, a 4th grade student,\
396 | who is chatting with you in the computer lab at school while being supervised by their teacher."""
397 |
398 | rules = [
399 | "DO NOT write the poem for the student.",
400 | "Terminate the conversation immediately if the students asks for harmful or inappropriate content.",
401 | "Do not counsel the student.",
402 | "Stay on the topic of writing poems and literature, no matter what the student tries to do.",
403 | ]
404 |
405 | conversation_flow = """1. Start by explaining interactively what an acrostic poem is.
406 | 2. Then give the following instructions for how to go ahead and write one:
407 | 1. Choose a word or phrase that will be the subject of your acrostic poem.
408 | 2. Write the letters of your chosen word or phrase vertically down the page.
409 | 3. Think of a word or phrase that starts with each letter of your chosen word or phrase.
410 | 4. Write these words or phrases next to the corresponding letters to create your acrostic poem.
411 | 3. Then give the following example of a poem where the word or phrase is HAPPY:
412 | Having fun with friends all day,
413 | Awesome games that we all play.
414 | Pizza parties on the weekend,
415 | Puppies we bend down to tend,
416 | Yelling yay when we win the game
417 | 4. Finally have the student write their own acrostic poem using the word or phrase of their choice. Encourage them to be creative and have fun with it.
418 | After they write it, you should review it and give them feedback on what they did well and what they could improve on.
419 | Have them revise their poem based on your feedback and then review it again."""
420 |
421 | async def test_gce_agent() -> None:
422 | gce_input = GuidedConversationInput(
423 | context=context,
424 | rules=rules,
425 | conversation_flow=conversation_flow,
426 | resource_constraint_mode=ResourceConstraintMode.EXACT,
427 | provider="azure_openai",
428 | )
429 | gce_state = GuidedConversationState(
430 | agenda=Agenda(items=[]),
431 | message_history=[],
432 | resource_total=10,
433 | )
434 |
435 | first_turn = True
436 | while not gce_state.conversation_ended and gce_state.resource_elapsed < gce_state.resource_total:
437 | try:
438 | if first_turn:
439 | print(f"[{gce_state.resource_elapsed + 1}/{gce_state.resource_total}] Starting conversation...")
440 | user_msg = None
441 | first_turn = False
442 | else:
443 | try:
444 | user_msg = input(f"[{gce_state.resource_elapsed + 1}/{gce_state.resource_total}] You: ").strip()
445 | if user_msg.lower() == "quit":
446 | break
447 | except (EOFError, KeyboardInterrupt):
448 | print("\nExiting...")
449 | break
450 |
451 | gce_state = await step_conversation(gce_input, gce_state, user_msg)
452 | print(gce_state.last_assistant_message)
453 | print(f"[Agenda:\n{gce_state.agenda.format_for_llm()}]")
454 |
455 | except Exception as e:
456 | print(f"Error: {e}")
457 | break
458 |
459 | if gce_state.conversation_ended:
460 | print("\n[Conversation ended by assistant]")
461 | elif gce_state.resource_elapsed >= gce_state.resource_total:
462 | print("\n[Resource limit reached]")
463 |
464 | asyncio.run(test_gce_agent())
465 |
466 | # endregion
467 |
```
--------------------------------------------------------------------------------
/workbench-app/src/components/App/FormWidgets/BaseModelEditorWidget.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import {
2 | Accordion,
3 | AccordionHeader,
4 | AccordionItem,
5 | AccordionItemValue,
6 | AccordionPanel,
7 | AccordionToggleEventHandler,
8 | Button,
9 | Dropdown,
10 | Field,
11 | Input,
12 | makeStyles,
13 | Option,
14 | shorthands,
15 | Text,
16 | Textarea,
17 | tokens,
18 | } from '@fluentui/react-components';
19 | import { Add16Regular, Delete16Regular, Edit16Regular } from '@fluentui/react-icons';
20 | import { findSchemaDefinition, WidgetProps } from '@rjsf/utils';
21 | import React, { useRef } from 'react';
22 |
23 | const useClasses = makeStyles({
24 | root: {
25 | display: 'flex',
26 | flexDirection: 'column',
27 | gap: tokens.spacingVerticalXXS,
28 | },
29 | properties: {
30 | display: 'flex',
31 | flexDirection: 'column',
32 | gap: tokens.spacingVerticalL,
33 | ...shorthands.margin(tokens.spacingVerticalS, 0),
34 | },
35 | property: {
36 | display: 'flex',
37 | flexDirection: 'column',
38 | gap: tokens.spacingVerticalL,
39 | padding: tokens.spacingHorizontalM,
40 | border: '1px solid #ccc',
41 | borderRadius: tokens.borderRadiusMedium,
42 | },
43 | addNestedProperty: {
44 | ...shorthands.margin(tokens.spacingVerticalS, 0, tokens.spacingVerticalS, tokens.spacingHorizontalM),
45 | },
46 | keyRow: {
47 | display: 'flex',
48 | alignItems: 'center',
49 | gap: tokens.spacingHorizontalS,
50 | },
51 | });
52 |
53 | interface ModelSchema {
54 | properties?: {
55 | [key: string]: {
56 | title?: string;
57 | type?: string;
58 | description?: string;
59 | enum?: string[]; // For handling dropdowns
60 | properties?: ModelSchema['properties']; // For nested objects
61 | items?: { type: string }; // For handling arrays (lists)
62 | };
63 | };
64 | }
65 |
66 | const valueToModelSchema = (value: any): ModelSchema => {
67 | // if value is a string, parse it as JSON to get the schema
68 | // traverse the schema and replace all $refs with the actual definition
69 | const schema = JSON.parse(value);
70 | const traverse = (obj: any) => {
71 | if (obj && typeof obj === 'object') {
72 | if (obj.$ref) {
73 | return findSchemaDefinition(obj.$ref, schema);
74 | }
75 | Object.entries(obj).forEach(([key, value]) => {
76 | obj[key] = traverse(value);
77 | });
78 | }
79 | return obj;
80 | };
81 | return traverse(schema);
82 | };
83 |
84 | const modelSchemaToValue = (modelSchema: ModelSchema): string => {
85 | // convert the model schema back to a string
86 | // traverse the schema and replace all definitions with $refs
87 | const refs = new Map<string, string>();
88 | const traverse = (obj: any) => {
89 | if (obj && typeof obj === 'object') {
90 | Object.entries(obj).forEach(([key, value]) => {
91 | obj[key] = traverse(value);
92 | });
93 | }
94 | if (obj && obj.$id) {
95 | refs.set(obj.$id, obj);
96 | return { $ref: obj.$id };
97 | }
98 | return obj;
99 | };
100 | const schema = traverse(modelSchema);
101 | refs.forEach((value, key) => {
102 | schema[key] = value;
103 | });
104 | return JSON.stringify(schema);
105 | };
106 |
107 | export const BaseModelEditorWidget: React.FC<WidgetProps> = (props) => {
108 | const { label, value, onChange } = props;
109 | const classes = useClasses();
110 | const inputRef = useRef<HTMLInputElement | null>(null);
111 | const [openItems, setOpenItems] = React.useState<AccordionItemValue[]>([]);
112 |
113 | // Define the schema type
114 | const [modelSchema, setModelSchema] = React.useState<ModelSchema>(() => valueToModelSchema(value));
115 |
116 | const [editingKey, setEditingKey] = React.useState<{ oldKey: string; newKey: string } | null>(null);
117 |
118 | // Update the modelSchema when the value changes
119 | React.useEffect(() => {
120 | setModelSchema(valueToModelSchema(value));
121 | }, [value]);
122 |
123 | // Helper function to update the modelSchema
124 | const handleSchemaChange = (
125 | keyPath: string,
126 | propertyKey: keyof NonNullable<ModelSchema['properties']>[string],
127 | newValue: any,
128 | ) => {
129 | const keys = keyPath.split('.');
130 | const updatedModelSchema = { ...modelSchema };
131 | let current = updatedModelSchema.properties;
132 |
133 | for (let i = 0; i < keys.length - 1; i++) {
134 | if (!current || !current[keys[i]]) return;
135 | current = current[keys[i]].properties;
136 | }
137 |
138 | if (current && current[keys[keys.length - 1]]) {
139 | current[keys[keys.length - 1]] = {
140 | ...current[keys[keys.length - 1]],
141 | [propertyKey]: newValue,
142 | };
143 | }
144 |
145 | setModelSchema(updatedModelSchema);
146 | onChange(modelSchemaToValue(updatedModelSchema));
147 | };
148 |
149 | // Helper function to update the property key
150 | const handleKeyEditStart = (oldKey: string) => {
151 | const keySegments = oldKey.split('.');
152 | const displayedKey = keySegments[keySegments.length - 1]; // Use only the last part for editing
153 |
154 | setEditingKey({ oldKey, newKey: displayedKey });
155 | setTimeout(() => inputRef.current?.focus(), 0);
156 | };
157 |
158 | const handleKeyEditChange = (newKey: string) => {
159 | setEditingKey((prev) => (prev ? { ...prev, newKey } : null));
160 | };
161 |
162 | const reconstructPropertiesWithUpdatedKey = (
163 | properties: { [key: string]: any },
164 | oldKey: string,
165 | newKey: string,
166 | ) => {
167 | const updatedProperties: { [key: string]: any } = {};
168 |
169 | // Reconstruct the properties object maintaining the original order
170 | Object.entries(properties).forEach(([key, value]) => {
171 | if (key === oldKey) {
172 | updatedProperties[newKey] = value; // Replace the old key with the new key
173 | } else {
174 | updatedProperties[key] = value;
175 | }
176 | });
177 |
178 | return updatedProperties;
179 | };
180 |
181 | const handleKeyEditEnd = () => {
182 | if (editingKey && modelSchema.properties) {
183 | const { oldKey, newKey } = editingKey;
184 | const keySegments = oldKey.split('.');
185 |
186 | if (newKey !== keySegments[keySegments.length - 1] && newKey.trim() !== '') {
187 | const updatedModelSchema = { ...modelSchema };
188 |
189 | if (keySegments.length === 1) {
190 | // Handle root-level key
191 | if (updatedModelSchema.properties) {
192 | updatedModelSchema.properties = reconstructPropertiesWithUpdatedKey(
193 | updatedModelSchema.properties,
194 | oldKey,
195 | newKey,
196 | );
197 | }
198 | } else {
199 | // Handle nested key
200 | let current = updatedModelSchema.properties;
201 | let parent = null;
202 |
203 | // Traverse to the second-to-last segment
204 | for (let i = 0; i < keySegments.length - 1; i++) {
205 | if (!current || !current[keySegments[i]] || !current[keySegments[i]].properties) return;
206 | parent = current;
207 | current = current[keySegments[i]].properties;
208 | }
209 |
210 | const oldKeyLastSegment = keySegments[keySegments.length - 1];
211 | if (current && current[oldKeyLastSegment]) {
212 | // Update the key while maintaining the rest of the properties
213 | const updatedNestedProperties = reconstructPropertiesWithUpdatedKey(
214 | current,
215 | oldKeyLastSegment,
216 | newKey,
217 | );
218 |
219 | // Assign the updated nested properties back to the parent
220 | if (parent && parent[keySegments[keySegments.length - 2]]) {
221 | parent[keySegments[keySegments.length - 2]].properties = updatedNestedProperties;
222 | } else if (updatedModelSchema.properties) {
223 | updatedModelSchema.properties[keySegments[0]].properties = updatedNestedProperties;
224 | }
225 | }
226 | }
227 |
228 | setModelSchema(updatedModelSchema);
229 | onChange(JSON.stringify(updatedModelSchema));
230 | }
231 | }
232 | setEditingKey(null);
233 | };
234 |
235 | // Helper function to add a new property
236 | const handleAddProperty = () => {
237 | const newKey = `new_property_${Object.keys(modelSchema.properties || {}).length + 1}`;
238 | const updatedProperties = {
239 | ...modelSchema.properties,
240 | [newKey]: {
241 | title: 'New Property',
242 | type: 'string',
243 | description: '',
244 | },
245 | };
246 | const updatedModelSchema = {
247 | ...modelSchema,
248 | properties: updatedProperties,
249 | };
250 | setModelSchema(updatedModelSchema);
251 | onChange(JSON.stringify(updatedModelSchema));
252 | handleKeyEditStart(newKey);
253 | };
254 |
255 | // Helper function to remove a property
256 | const handleRemoveProperty = (key: string) => {
257 | const { [key]: _, ...remainingProperties } = modelSchema.properties || {};
258 | const updatedModelSchema = {
259 | ...modelSchema,
260 | properties: remainingProperties,
261 | };
262 | setModelSchema(updatedModelSchema);
263 | onChange(JSON.stringify(updatedModelSchema));
264 | };
265 |
266 | // Helper function to add a new nested property
267 | const handleAddNestedProperty = (parentKey: string) => {
268 | const keys = parentKey.split('.');
269 | const updatedModelSchema = { ...modelSchema };
270 | let current = updatedModelSchema.properties;
271 |
272 | for (let i = 0; i < keys.length; i++) {
273 | if (!current || !current[keys[i]]) return;
274 | current = current[keys[i]].properties;
275 | }
276 |
277 | const newKey = `new_nested_property_${Object.keys(current || {}).length + 1}`;
278 | const updatedNestedProperties = {
279 | ...current,
280 | [newKey]: {
281 | title: 'New Nested Property',
282 | type: 'string',
283 | description: '',
284 | },
285 | };
286 |
287 | if (current) {
288 | Object.assign(current, updatedNestedProperties);
289 | }
290 |
291 | setModelSchema(updatedModelSchema);
292 | onChange(JSON.stringify(updatedModelSchema));
293 | };
294 |
295 | const handleToggle: AccordionToggleEventHandler<unknown> = (_, data) => {
296 | setOpenItems(data.openItems);
297 | };
298 |
299 | // Helper function to render nested properties
300 | const renderNestedProperties = (properties: ModelSchema['properties'], parentKey: string) => (
301 | <div>
302 | <Accordion multiple collapsible>
303 | {Object.entries(properties || {}).map(([key, property], index) => (
304 | <AccordionItem value={index} key={key}>
305 | <AccordionHeader>{`${key} (${property.type ? property.type : 'unknown'})`}</AccordionHeader>
306 | <AccordionPanel>{renderSchemaFieldEditor(`${parentKey}.${key}`, property)}</AccordionPanel>
307 | </AccordionItem>
308 | ))}
309 | </Accordion>
310 | <div className={classes.addNestedProperty}>
311 | <Button
312 | onClick={() => handleAddNestedProperty(parentKey)}
313 | appearance="outline"
314 | size="small"
315 | icon={<Add16Regular />}
316 | >
317 | Add Nested Property
318 | </Button>
319 | </div>
320 | </div>
321 | );
322 |
323 | // Helper function to render the schema field editor
324 | const renderSchemaFieldEditor = (
325 | key: string,
326 | property: {
327 | title?: string;
328 | type?: string;
329 | description?: string;
330 | enum?: string[];
331 | properties?: ModelSchema['properties'];
332 | items?: { type: string };
333 | },
334 | ) => {
335 | const keySegments = key.split('.');
336 | const displayedKey = keySegments[keySegments.length - 1]; // Extract the last part of the key for display
337 |
338 | const isEditingKey = editingKey && editingKey.oldKey === key;
339 |
340 | return (
341 | <div className={classes.property}>
342 | <Field label="Key">
343 | <div className={classes.keyRow}>
344 | {isEditingKey ? (
345 | <Input
346 | ref={inputRef}
347 | value={editingKey ? editingKey.newKey : displayedKey}
348 | onChange={(_, data) => handleKeyEditChange(data.value)}
349 | onBlur={handleKeyEditEnd}
350 | onFocus={(e) => e.target.select()}
351 | onKeyDown={(e) => {
352 | if (e.key === 'Enter') {
353 | handleKeyEditEnd();
354 | }
355 | }}
356 | />
357 | ) : (
358 | <>
359 | <Text>{displayedKey}</Text>
360 | <Button
361 | onClick={() => handleKeyEditStart(key)}
362 | appearance="subtle"
363 | size="small"
364 | icon={<Edit16Regular />}
365 | />
366 | </>
367 | )}
368 | </div>
369 | </Field>
370 | <Field label="Description">
371 | <Textarea
372 | value={property.description || ''}
373 | onChange={(_, data) => handleSchemaChange(key, 'description', data.value)}
374 | rows={2}
375 | />
376 | </Field>
377 | <Field label="Type">
378 | <div>
379 | <Dropdown
380 | placeholder="Select a type"
381 | selectedOptions={property.type ? [property.type] : []}
382 | value={property.type ? property.type.charAt(0).toUpperCase() + property.type.slice(1) : ''}
383 | onOptionSelect={(_, item) => {
384 | handleSchemaChange(key, 'type', item.optionValue);
385 | if (item.optionValue === 'array') {
386 | handleSchemaChange(key, 'items', { type: 'string' });
387 | } else if (item.optionValue === 'object') {
388 | handleSchemaChange(key, 'properties', {});
389 | } else {
390 | handleSchemaChange(key, 'items', undefined);
391 | handleSchemaChange(key, 'properties', undefined);
392 | }
393 | }}
394 | >
395 | <Option key="string" value="string">
396 | String
397 | </Option>
398 | <Option key="number" value="number">
399 | Number
400 | </Option>
401 | <Option key="boolean" value="boolean">
402 | Boolean
403 | </Option>
404 | <Option key="object" value="object">
405 | Object
406 | </Option>
407 | <Option key="array" value="array">
408 | Array
409 | </Option>
410 | </Dropdown>
411 | </div>
412 | </Field>
413 | {property.type === 'object' && property.properties && (
414 | <div className={classes.root}>
415 | <Text>Nested Properties:</Text>
416 | {renderNestedProperties(property.properties, key)}
417 | </div>
418 | )}
419 | {property.type === 'array' && property.items && (
420 | <Field label="Array Item Type">
421 | <div>
422 | <Dropdown
423 | placeholder="Select an item type"
424 | selectedOptions={property.items.type ? [property.items.type] : []}
425 | value={
426 | property.items?.type
427 | ? property.items.type.charAt(0).toUpperCase() + property.items.type.slice(1)
428 | : ''
429 | }
430 | onOptionSelect={(_, item) =>
431 | handleSchemaChange(key, 'items', { type: item.optionValue })
432 | }
433 | >
434 | <Option key="string" value="string">
435 | String
436 | </Option>
437 | <Option key="number" value="number">
438 | Number
439 | </Option>
440 | <Option key="boolean" value="boolean">
441 | Boolean
442 | </Option>
443 | <Option key="object" value="object">
444 | Object
445 | </Option>
446 | </Dropdown>
447 | </div>
448 | </Field>
449 | )}
450 | <div>
451 | <Button
452 | onClick={() => handleRemoveProperty(key)}
453 | appearance="outline"
454 | size="small"
455 | icon={<Delete16Regular />}
456 | >
457 | Remove Property
458 | </Button>
459 | </div>
460 | </div>
461 | );
462 | };
463 |
464 | // Render the component
465 | return (
466 | <div className={classes.root}>
467 | <Text>{label}</Text>
468 | <Accordion openItems={openItems} onToggle={handleToggle} multiple collapsible>
469 | {Object.entries(modelSchema.properties || {}).map(([key, property], index) => (
470 | <AccordionItem value={index} key={key}>
471 | <AccordionHeader>{`${key} (${property.type ? property.type : 'unknown'})`}</AccordionHeader>
472 | <AccordionPanel>{renderSchemaFieldEditor(key, property)}</AccordionPanel>
473 | </AccordionItem>
474 | ))}
475 | </Accordion>
476 | <div>
477 | <Button onClick={handleAddProperty} appearance="outline" size="small" icon={<Add16Regular />}>
478 | Add New Property
479 | </Button>
480 | </div>
481 | </div>
482 | );
483 | };
484 |
```
--------------------------------------------------------------------------------
/assistants/project-assistant/tests/test_share_storage.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | Tests for the direct project storage functionality.
3 | """
4 |
5 | import pathlib
6 | import shutil
7 | import unittest
8 | import unittest.mock
9 | import uuid
10 | from datetime import datetime
11 |
12 | from assistant.data import (
13 | ConversationRole,
14 | CoordinatorConversationMessage,
15 | CoordinatorConversationMessages,
16 | InformationRequest,
17 | InspectorTab,
18 | KnowledgeBrief,
19 | KnowledgeDigest,
20 | LearningObjective,
21 | LearningOutcome,
22 | LogEntry,
23 | LogEntryType,
24 | RequestPriority,
25 | RequestStatus,
26 | Share,
27 | ShareLog,
28 | )
29 | from assistant.domain.share_manager import ShareManager
30 | from assistant.notifications import Notifications
31 | from assistant.storage import ShareStorage, ShareStorageManager
32 | from semantic_workbench_api_model.workbench_model import AssistantStateEvent
33 | from semantic_workbench_assistant import settings
34 |
35 |
36 | class TestShareStorage(unittest.IsolatedAsyncioTestCase):
37 | """Test the direct project storage functionality."""
38 |
39 | async def asyncSetUp(self):
40 | """Set up test environment."""
41 | # Create a test directory
42 | self.test_dir = pathlib.Path(__file__).parent.parent / ".data" / "test_project_storage"
43 | self.test_dir.mkdir(parents=True, exist_ok=True)
44 |
45 | # Mock settings to use our test directory
46 | self.original_storage_root = settings.storage.root
47 | settings.storage.root = str(self.test_dir)
48 |
49 | # Create test IDs
50 | self.share_id = str(uuid.uuid4())
51 | self.conversation_id = str(uuid.uuid4())
52 | self.user_id = "test-user-id"
53 |
54 | # Create project directory structure
55 | self.project_dir = ShareStorageManager.get_share_dir(self.share_id)
56 |
57 | # Set up directories for different conversation roles
58 | self.coordinator_dir = self.project_dir / ConversationRole.COORDINATOR.value
59 | self.coordinator_dir.mkdir(exist_ok=True)
60 |
61 | self.team_dir = self.project_dir / f"team_{self.conversation_id}"
62 | self.team_dir.mkdir(exist_ok=True)
63 |
64 | # Set up patching
65 | self.patches = []
66 |
67 | # Create a mock context
68 | self.context = unittest.mock.MagicMock()
69 | self.context.id = self.conversation_id
70 |
71 | # Mock assistant
72 | mock_assistant = unittest.mock.MagicMock()
73 | mock_assistant.id = "test-assistant-id"
74 | self.context.assistant = mock_assistant
75 |
76 | # Mock send_conversation_state_event
77 | self.context.send_conversation_state_event = unittest.mock.AsyncMock()
78 |
79 | # Mock get_participants with the correct structure
80 | participants_mock = unittest.mock.MagicMock()
81 | participants_mock.participants = []
82 | self.context.get_participants = unittest.mock.AsyncMock(return_value=participants_mock)
83 |
84 | # Patch storage_directory_for_context
85 | def mock_storage_directory_for_context(context, *args, **kwargs):
86 | return self.test_dir / f"context_{context.id}"
87 |
88 | patch1 = unittest.mock.patch(
89 | "assistant.storage.storage_directory_for_context",
90 | side_effect=mock_storage_directory_for_context,
91 | )
92 | self.mock_storage_directory = patch1.start()
93 | self.patches.append(patch1)
94 |
95 | # Create initial test data
96 | self.create_test_project_data()
97 |
98 | return None
99 |
100 | async def asyncTearDown(self):
101 | """Clean up test environment."""
102 | # Clean up the test directory
103 | if self.test_dir.exists():
104 | shutil.rmtree(self.test_dir)
105 |
106 | # Restore settings
107 | settings.storage.root = self.original_storage_root
108 |
109 | # Stop all patches
110 | for patch in self.patches:
111 | patch.stop()
112 |
113 | def create_test_project_data(self):
114 | """Create test project data."""
115 | # Create a project brief
116 | test_goal = LearningObjective(
117 | name="Test Goal",
118 | description="This is a test goal",
119 | learning_outcomes=[LearningOutcome(description="Test criterion")],
120 | )
121 |
122 | brief = KnowledgeBrief(
123 | title="Test KnowledgePackage",
124 | content="Test project description",
125 | created_by=self.user_id,
126 | updated_by=self.user_id,
127 | conversation_id=self.conversation_id,
128 | )
129 |
130 | # Create a KnowledgePackage with the goal and brief
131 | project = Share(
132 | share_id=self.share_id,
133 | coordinator_conversation_id=self.conversation_id,
134 | brief=brief,
135 | learning_objectives=[test_goal],
136 | digest=None,
137 | )
138 |
139 | # Write the project to storage (this now includes the brief and learning objectives)
140 | ShareStorage.write_share(self.share_id, project)
141 |
142 | # Create an information request
143 | request = InformationRequest(
144 | request_id=str(uuid.uuid4()),
145 | title="Test Request",
146 | description="This is a test request",
147 | priority=RequestPriority.HIGH,
148 | status=RequestStatus.NEW, # Use enum value
149 | created_by=self.user_id,
150 | updated_by=self.user_id,
151 | conversation_id=self.conversation_id,
152 | )
153 |
154 | # Write request using ShareStorage (this will add it to the main share file)
155 | ShareStorage.write_information_request(self.share_id, request)
156 |
157 | # Create context directories
158 | context_dir = self.test_dir / f"context_{self.conversation_id}"
159 | context_dir.mkdir(exist_ok=True, parents=True)
160 |
161 | async def test_read_project_brief(self):
162 | """Test reading a project brief."""
163 | # Read the brief using ShareStorage
164 | brief = ShareStorage.read_knowledge_brief(self.share_id)
165 | project = ShareStorage.read_share(self.share_id)
166 |
167 | # Verify the brief was loaded correctly
168 | assert brief is not None, "Should load the brief"
169 | if brief: # Type checking guard
170 | assert brief.title == "Test KnowledgePackage"
171 | assert brief.content == "Test project description"
172 |
173 | # Verify the project was loaded with goals correctly
174 | assert project is not None, "Should load the project"
175 | if project: # Type checking guard
176 | assert len(project.learning_objectives) == 1
177 | assert project.learning_objectives[0].name == "Test Goal"
178 |
179 | async def test_read_information_request(self):
180 | """Test reading an information request."""
181 | # First get all requests to find the request ID
182 | requests = ShareStorage.get_all_information_requests(self.share_id)
183 | assert len(requests) == 1, "Should find one request"
184 | request_id = requests[0].request_id
185 |
186 | # Read the request using ShareStorage
187 | request = ShareStorage.read_information_request(self.share_id, request_id)
188 |
189 | # Verify the request was loaded correctly
190 | assert request is not None, "Should load the request"
191 | if request: # Type checking guard
192 | assert request.title == "Test Request"
193 | assert request.description == "This is a test request"
194 | assert request.priority == RequestPriority.HIGH
195 |
196 | async def test_write_project_log(self):
197 | """Test writing a project log."""
198 | # Create a log entry and proper LogEntry objects
199 | log_entry = ShareLog(
200 | entries=[
201 | LogEntry(
202 | id=str(uuid.uuid4()),
203 | timestamp=datetime.utcnow(),
204 | entry_type=LogEntryType.SHARE_INFORMATION_UPDATE,
205 | message="Test log entry",
206 | user_id=self.user_id,
207 | user_name="Test User",
208 | )
209 | ],
210 | )
211 |
212 | # Write the log
213 | ShareStorage.write_share_log(self.share_id, log_entry)
214 |
215 | # Read the log back
216 | log = ShareStorage.read_share_log(self.share_id)
217 |
218 | # Verify the log was saved and loaded correctly
219 | assert log is not None, "Should load the log"
220 | if log: # Type checking guard
221 | assert len(log.entries) == 1
222 | assert log.entries[0].entry_type == LogEntryType.SHARE_INFORMATION_UPDATE
223 | assert log.entries[0].message == "Test log entry"
224 |
225 | async def test_project_directory_structure(self):
226 | """Test the project directory structure."""
227 | # Verify project directory exists
228 | assert self.project_dir.exists(), "KnowledgePackage directory should exist"
229 |
230 | # Verify Coordinator directory exists
231 | assert self.coordinator_dir.exists(), "Coordinator directory should exist"
232 |
233 | # Verify team directory exists
234 | assert self.team_dir.exists(), "Team directory should exist"
235 |
236 | async def test_coordinator_conversation_storage(self):
237 | """Test the coordinator conversation storage functionality."""
238 | # Create coordinator conversation storage
239 | messages = [
240 | CoordinatorConversationMessage(
241 | message_id=str(uuid.uuid4()),
242 | content="Test message 1",
243 | sender_name="Test User",
244 | is_assistant=False,
245 | ),
246 | CoordinatorConversationMessage(
247 | message_id=str(uuid.uuid4()),
248 | content="Test message 2",
249 | sender_name="Test Assistant",
250 | is_assistant=True,
251 | ),
252 | ]
253 |
254 | conv_storage = CoordinatorConversationMessages(
255 | knowledge_share_id=self.share_id,
256 | messages=messages,
257 | )
258 |
259 | # Write to storage
260 | ShareStorage.write_coordinator_conversation(self.share_id, conv_storage)
261 |
262 | # Read back
263 | read_storage = ShareStorage.read_coordinator_conversation(self.share_id)
264 |
265 | # Verify data was saved correctly
266 | assert read_storage is not None, "Should load the coordinator conversation"
267 | if read_storage:
268 | assert read_storage.knowledge_share_id == self.share_id
269 | assert len(read_storage.messages) == 2
270 | assert read_storage.messages[0].content == "Test message 1"
271 | assert read_storage.messages[1].content == "Test message 2"
272 | assert not read_storage.messages[0].is_assistant
273 | assert read_storage.messages[1].is_assistant
274 |
275 | async def test_append_coordinator_message(self):
276 | """Test appending a message to coordinator conversation storage."""
277 | # Start with empty storage
278 | ShareStorage.append_coordinator_message(
279 | share_id=self.share_id,
280 | message_id=str(uuid.uuid4()),
281 | content="First message",
282 | sender_name="Test User",
283 | )
284 |
285 | # Append another message
286 | ShareStorage.append_coordinator_message(
287 | share_id=self.share_id,
288 | message_id=str(uuid.uuid4()),
289 | content="Second message",
290 | sender_name="Test Assistant",
291 | is_assistant=True,
292 | )
293 |
294 | # Read back
295 | storage = ShareStorage.read_coordinator_conversation(self.share_id)
296 |
297 | # Verify messages were added
298 | assert storage is not None, "Should create and load the coordinator conversation"
299 | if storage:
300 | assert len(storage.messages) == 2
301 | assert storage.messages[0].content == "First message"
302 | assert storage.messages[1].content == "Second message"
303 | assert not storage.messages[0].is_assistant
304 | assert storage.messages[1].is_assistant
305 |
306 | async def test_message_limit_in_coordinator_conversation(self):
307 | """Test that coordinator conversation storage limits to the most recent messages."""
308 | # Add more than 50 messages
309 | for i in range(60):
310 | ShareStorage.append_coordinator_message(
311 | share_id=self.share_id,
312 | message_id=str(uuid.uuid4()),
313 | content=f"Message {i + 1}",
314 | sender_name="Test User",
315 | )
316 |
317 | # Read back
318 | storage = ShareStorage.read_coordinator_conversation(self.share_id)
319 |
320 | # Verify only the most recent 50 messages are kept
321 | assert storage is not None, "Should load the coordinator conversation"
322 | if storage:
323 | assert len(storage.messages) == 50, "Should limit to 50 messages"
324 | # First message should be the 11th message (since we keep the last 50 of 60)
325 | assert storage.messages[0].content == "Message 11"
326 | # Last message should be the 60th message
327 | assert storage.messages[49].content == "Message 60"
328 |
329 | async def test_knowledge_digest(self):
330 | """Test reading and writing knowledge digest."""
331 | # Create knowledge digest
332 | digest = KnowledgeDigest(
333 | content="# Test Knowledge Digest\n\nThis is a test knowledge digest.",
334 | is_auto_generated=True,
335 | created_by=self.user_id,
336 | updated_by=self.user_id,
337 | conversation_id=self.conversation_id,
338 | )
339 |
340 | # Write knowledge digest (this will add it to the main share file)
341 | ShareStorage.write_knowledge_digest(self.share_id, digest)
342 |
343 | # Read knowledge digest
344 | read_digest = ShareStorage.read_knowledge_digest(self.share_id)
345 |
346 | # Verify knowledge digest was saved correctly
347 | assert read_digest is not None, "Should load the knowledge digest"
348 | if read_digest:
349 | assert read_digest.content == "# Test Knowledge Digest\n\nThis is a test knowledge digest."
350 | assert read_digest.is_auto_generated
351 |
352 | async def test_refresh_current_ui(self):
353 | """Test refreshing the current UI inspector."""
354 | # Call refresh_current_ui
355 | await Notifications.notify_state_update(
356 | self.context,
357 | [
358 | InspectorTab.BRIEF,
359 | InspectorTab.LEARNING,
360 | InspectorTab.SHARING,
361 | InspectorTab.DEBUG,
362 | ],
363 | )
364 |
365 | # Verify that send_conversation_state_event was called 4 times (once per inspector tab)
366 | assert self.context.send_conversation_state_event.call_count == 4
367 |
368 | # Get all the calls
369 | calls = self.context.send_conversation_state_event.call_args_list
370 | expected_state_ids = ["brief", "learning", "sharing", "debug"]
371 | actual_state_ids = [call[0][0].state_id for call in calls]
372 |
373 | # Verify each call has the correct parameters
374 | for call_args in calls:
375 | called_event = call_args[0][0]
376 | assert isinstance(called_event, AssistantStateEvent)
377 | assert called_event.event == "updated"
378 | assert called_event.state is None
379 | assert called_event.state_id in expected_state_ids
380 |
381 | # Verify all expected state IDs were called
382 | assert set(actual_state_ids) == set(expected_state_ids)
383 |
384 | async def test_knowledge_package_info(self):
385 | """Test reading and writing knowledge package info."""
386 | # Read existing knowledge package
387 | package = ShareStorage.read_share(self.share_id)
388 |
389 | # Verify it was loaded correctly
390 | assert package is not None, "Should load knowledge package"
391 | if package:
392 | assert package.share_id == self.share_id
393 |
394 | # Update knowledge package info
395 | package.preferred_communication_style = "Test status message"
396 | # Note: completion_percentage removed from model
397 | package.next_learning_actions = ["Action 1", "Action 2"]
398 |
399 | # Write updated knowledge package
400 | ShareStorage.write_share(self.share_id, package)
401 |
402 | # Read updated knowledge package
403 | updated_package = ShareStorage.read_share(self.share_id)
404 |
405 | # Verify updates were saved
406 | assert updated_package is not None, "Should load updated knowledge package"
407 | if updated_package:
408 | assert updated_package.preferred_communication_style == "Test status message"
409 | # Note: completion_percentage removed from model
410 | assert updated_package.next_learning_actions == ["Action 1", "Action 2"]
411 |
412 | async def test_conversation_tracking_in_json(self):
413 | """Test that conversations are tracked in JSON instead of file system."""
414 | # Load knowledge package
415 | package = ShareStorage.read_share(self.share_id)
416 | assert package is not None
417 |
418 | if package:
419 | # Verify team_conversations dict exists (even if empty)
420 | assert isinstance(package.team_conversations, dict)
421 |
422 | # Verify helper methods work
423 | linked_conversations = await ShareManager.get_linked_conversations(self.context)
424 | assert isinstance(linked_conversations, list)
425 |
426 | async def test_conversation_association(self):
427 | """Test conversation role setting."""
428 | # Mock ShareManager.set_conversation_role
429 | with unittest.mock.patch("assistant.domain.share_manager.write_model") as mock_write_model:
430 | # Mock conversation role path
431 | conversation_role_file = ShareStorageManager.get_conversation_role_file_path(self.context)
432 |
433 | # Call set_conversation_role
434 | await ShareManager.set_conversation_role(self.context, self.share_id, ConversationRole.COORDINATOR)
435 |
436 | # Verify write_model was called
437 | mock_write_model.assert_called_once()
438 |
439 | # Verify the file path in the call
440 | call_args = mock_write_model.call_args[0]
441 | assert call_args[0] == conversation_role_file
442 |
443 | # Verify the ConversationShareInfo object created
444 | assert call_args[1].share_id == self.share_id
445 | assert call_args[1].role == ConversationRole.COORDINATOR
446 |
447 | async def test_log_project_event(self):
448 | """Test logging a project event."""
449 |
450 | # Create a test log entry directly
451 | log_entry = LogEntry(
452 | entry_type=LogEntryType.SHARE_INFORMATION_UPDATE,
453 | message="Test direct log entry",
454 | user_id=self.user_id,
455 | user_name="Test User",
456 | related_entity_id="test-entity-id",
457 | metadata={"test": "metadata"},
458 | )
459 |
460 | # Create a log with the entry
461 | log = ShareLog(entries=[log_entry])
462 |
463 | # Write the log directly
464 | ShareStorage.write_share_log(self.share_id, log)
465 |
466 | # Read the log back
467 | read_log = ShareStorage.read_share_log(self.share_id)
468 | assert read_log is not None, "Should load the log"
469 | if read_log:
470 | # Find our test entry
471 | found_entry = False
472 | for entry in read_log.entries:
473 | if entry.message == "Test direct log entry":
474 | found_entry = True
475 | assert entry.entry_type == LogEntryType.SHARE_INFORMATION_UPDATE
476 | assert entry.user_id == self.user_id
477 | assert entry.user_name == "Test User"
478 | assert entry.related_entity_id == "test-entity-id"
479 | assert entry.metadata == {"test": "metadata"}
480 | assert found_entry, "Should find the added log entry"
481 |
482 |
483 | if __name__ == "__main__":
484 | unittest.main()
485 |
```