This is page 110 of 145. Use http://codebase.md/microsoft/semanticworkbench?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .devcontainer
│ ├── .vscode
│ │ └── settings.json
│ ├── devcontainer.json
│ ├── OPTIMIZING_FOR_CODESPACES.md
│ ├── POST_SETUP_README.md
│ └── README.md
├── .dockerignore
├── .gitattributes
├── .github
│ ├── policheck.yml
│ └── workflows
│ ├── assistants-codespace-assistant.yml
│ ├── assistants-document-assistant.yml
│ ├── assistants-explorer-assistant.yml
│ ├── assistants-guided-conversation-assistant.yml
│ ├── assistants-knowledge-transfer-assistant.yml
│ ├── assistants-navigator-assistant.yml
│ ├── assistants-project-assistant.yml
│ ├── assistants-prospector-assistant.yml
│ ├── assistants-skill-assistant.yml
│ ├── libraries.yml
│ ├── mcp-server-giphy.yml
│ ├── mcp-server-memory-filesystem-edit.yml
│ ├── mcp-server-memory-user-bio.yml
│ ├── mcp-server-memory-whiteboard.yml
│ ├── mcp-server-open-deep-research-clone.yml
│ ├── mcp-server-web-research.yml
│ ├── workbench-app.yml
│ └── workbench-service.yml
├── .gitignore
├── .multi-root-tools
│ ├── Makefile
│ └── README.md
├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ └── settings.json
├── ai_context
│ └── generated
│ ├── ASPIRE_ORCHESTRATOR.md
│ ├── ASSISTANT_CODESPACE.md
│ ├── ASSISTANT_DOCUMENT.md
│ ├── ASSISTANT_NAVIGATOR.md
│ ├── ASSISTANT_PROJECT.md
│ ├── ASSISTANT_PROSPECTOR.md
│ ├── ASSISTANTS_OTHER.md
│ ├── ASSISTANTS_OVERVIEW.md
│ ├── CONFIGURATION.md
│ ├── DOTNET_LIBRARIES.md
│ ├── EXAMPLES.md
│ ├── MCP_SERVERS.md
│ ├── PYTHON_LIBRARIES_AI_CLIENTS.md
│ ├── PYTHON_LIBRARIES_CORE.md
│ ├── PYTHON_LIBRARIES_EXTENSIONS.md
│ ├── PYTHON_LIBRARIES_SKILLS.md
│ ├── PYTHON_LIBRARIES_SPECIALIZED.md
│ ├── TOOLS.md
│ ├── WORKBENCH_FRONTEND.md
│ └── WORKBENCH_SERVICE.md
├── aspire-orchestrator
│ ├── .editorconfig
│ ├── Aspire.AppHost
│ │ ├── .gitignore
│ │ ├── appsettings.json
│ │ ├── Aspire.AppHost.csproj
│ │ ├── Program.cs
│ │ └── Properties
│ │ └── launchSettings.json
│ ├── Aspire.Extensions
│ │ ├── Aspire.Extensions.csproj
│ │ ├── Dashboard.cs
│ │ ├── DockerFileExtensions.cs
│ │ ├── PathNormalizer.cs
│ │ ├── UvAppHostingExtensions.cs
│ │ ├── UvAppResource.cs
│ │ ├── VirtualEnvironment.cs
│ │ └── WorkbenchServiceHostingExtensions.cs
│ ├── Aspire.ServiceDefaults
│ │ ├── Aspire.ServiceDefaults.csproj
│ │ └── Extensions.cs
│ ├── README.md
│ ├── run.sh
│ ├── SemanticWorkbench.Aspire.sln
│ └── SemanticWorkbench.Aspire.sln.DotSettings
├── assistants
│ ├── codespace-assistant
│ │ ├── .claude
│ │ │ └── settings.local.json
│ │ ├── .env.example
│ │ ├── .vscode
│ │ │ ├── extensions.json
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── assistant
│ │ │ ├── __init__.py
│ │ │ ├── assets
│ │ │ │ ├── icon_context_transfer.svg
│ │ │ │ └── icon.svg
│ │ │ ├── chat.py
│ │ │ ├── config.py
│ │ │ ├── helpers.py
│ │ │ ├── response
│ │ │ │ ├── __init__.py
│ │ │ │ ├── completion_handler.py
│ │ │ │ ├── models.py
│ │ │ │ ├── request_builder.py
│ │ │ │ ├── response.py
│ │ │ │ ├── step_handler.py
│ │ │ │ └── utils
│ │ │ │ ├── __init__.py
│ │ │ │ ├── abbreviations.py
│ │ │ │ ├── formatting_utils.py
│ │ │ │ ├── message_utils.py
│ │ │ │ └── openai_utils.py
│ │ │ ├── text_includes
│ │ │ │ ├── card_content_context_transfer.md
│ │ │ │ ├── card_content.md
│ │ │ │ ├── codespace_assistant_info.md
│ │ │ │ ├── context_transfer_assistant_info.md
│ │ │ │ ├── guardrails_prompt.txt
│ │ │ │ ├── guidance_prompt_context_transfer.txt
│ │ │ │ ├── guidance_prompt.txt
│ │ │ │ ├── instruction_prompt_context_transfer.txt
│ │ │ │ └── instruction_prompt.txt
│ │ │ └── whiteboard
│ │ │ ├── __init__.py
│ │ │ ├── _inspector.py
│ │ │ └── _whiteboard.py
│ │ ├── assistant.code-workspace
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── document-assistant
│ │ ├── .env.example
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── assistant
│ │ │ ├── __init__.py
│ │ │ ├── assets
│ │ │ │ └── icon.svg
│ │ │ ├── chat.py
│ │ │ ├── config.py
│ │ │ ├── context_management
│ │ │ │ ├── __init__.py
│ │ │ │ └── inspector.py
│ │ │ ├── filesystem
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _convert.py
│ │ │ │ ├── _file_sources.py
│ │ │ │ ├── _filesystem.py
│ │ │ │ ├── _inspector.py
│ │ │ │ ├── _model.py
│ │ │ │ ├── _prompts.py
│ │ │ │ └── _tasks.py
│ │ │ ├── guidance
│ │ │ │ ├── __init__.py
│ │ │ │ ├── dynamic_ui_inspector.py
│ │ │ │ ├── guidance_config.py
│ │ │ │ ├── guidance_prompts.py
│ │ │ │ └── README.md
│ │ │ ├── response
│ │ │ │ ├── __init__.py
│ │ │ │ ├── completion_handler.py
│ │ │ │ ├── models.py
│ │ │ │ ├── prompts.py
│ │ │ │ ├── responder.py
│ │ │ │ └── utils
│ │ │ │ ├── __init__.py
│ │ │ │ ├── formatting_utils.py
│ │ │ │ ├── message_utils.py
│ │ │ │ ├── openai_utils.py
│ │ │ │ ├── tokens_tiktoken.py
│ │ │ │ └── workbench_messages.py
│ │ │ ├── text_includes
│ │ │ │ └── document_assistant_info.md
│ │ │ ├── types.py
│ │ │ └── whiteboard
│ │ │ ├── __init__.py
│ │ │ ├── _inspector.py
│ │ │ └── _whiteboard.py
│ │ ├── assistant.code-workspace
│ │ ├── CLAUDE.md
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_convert.py
│ │ │ └── test_data
│ │ │ ├── blank_image.png
│ │ │ ├── Formatting Test.docx
│ │ │ ├── sample_data.csv
│ │ │ ├── sample_data.xlsx
│ │ │ ├── sample_page.html
│ │ │ ├── sample_presentation.pptx
│ │ │ └── simple_pdf.pdf
│ │ └── uv.lock
│ ├── explorer-assistant
│ │ ├── .env.example
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── assistant
│ │ │ ├── __init__.py
│ │ │ ├── chat.py
│ │ │ ├── config.py
│ │ │ ├── helpers.py
│ │ │ ├── response
│ │ │ │ ├── __init__.py
│ │ │ │ ├── model.py
│ │ │ │ ├── response_anthropic.py
│ │ │ │ ├── response_openai.py
│ │ │ │ └── response.py
│ │ │ └── text_includes
│ │ │ └── guardrails_prompt.txt
│ │ ├── assistant.code-workspace
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── guided-conversation-assistant
│ │ ├── .env.example
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── assistant
│ │ │ ├── __init__.py
│ │ │ ├── agents
│ │ │ │ ├── guided_conversation
│ │ │ │ │ ├── config.py
│ │ │ │ │ ├── definition.py
│ │ │ │ │ └── definitions
│ │ │ │ │ ├── er_triage.py
│ │ │ │ │ ├── interview.py
│ │ │ │ │ ├── patient_intake.py
│ │ │ │ │ └── poem_feedback.py
│ │ │ │ └── guided_conversation_agent.py
│ │ │ ├── chat.py
│ │ │ ├── config.py
│ │ │ └── text_includes
│ │ │ └── guardrails_prompt.txt
│ │ ├── assistant.code-workspace
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── knowledge-transfer-assistant
│ │ ├── .claude
│ │ │ └── settings.local.json
│ │ ├── .env.example
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── assistant
│ │ │ ├── __init__.py
│ │ │ ├── agentic
│ │ │ │ ├── __init__.py
│ │ │ │ ├── analysis.py
│ │ │ │ ├── coordinator_support.py
│ │ │ │ └── team_welcome.py
│ │ │ ├── assets
│ │ │ │ ├── icon-knowledge-transfer.svg
│ │ │ │ └── icon.svg
│ │ │ ├── assistant.py
│ │ │ ├── common.py
│ │ │ ├── config.py
│ │ │ ├── conversation_clients.py
│ │ │ ├── conversation_share_link.py
│ │ │ ├── data.py
│ │ │ ├── domain
│ │ │ │ ├── __init__.py
│ │ │ │ ├── audience_manager.py
│ │ │ │ ├── information_request_manager.py
│ │ │ │ ├── knowledge_brief_manager.py
│ │ │ │ ├── knowledge_digest_manager.py
│ │ │ │ ├── learning_objectives_manager.py
│ │ │ │ └── share_manager.py
│ │ │ ├── files.py
│ │ │ ├── logging.py
│ │ │ ├── notifications.py
│ │ │ ├── respond.py
│ │ │ ├── storage_models.py
│ │ │ ├── storage.py
│ │ │ ├── string_utils.py
│ │ │ ├── text_includes
│ │ │ │ ├── assistant_info.md
│ │ │ │ ├── card_content.md
│ │ │ │ ├── coordinator_instructions.txt
│ │ │ │ ├── coordinator_role.txt
│ │ │ │ ├── knowledge_digest_instructions.txt
│ │ │ │ ├── knowledge_digest_prompt.txt
│ │ │ │ ├── share_information_request_detection.txt
│ │ │ │ ├── team_instructions.txt
│ │ │ │ ├── team_role.txt
│ │ │ │ └── welcome_message_generation.txt
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── base.py
│ │ │ │ ├── information_requests.py
│ │ │ │ ├── learning_objectives.py
│ │ │ │ ├── learning_outcomes.py
│ │ │ │ ├── progress_tracking.py
│ │ │ │ └── share_setup.py
│ │ │ ├── ui_tabs
│ │ │ │ ├── __init__.py
│ │ │ │ ├── brief.py
│ │ │ │ ├── common.py
│ │ │ │ ├── debug.py
│ │ │ │ ├── learning.py
│ │ │ │ └── sharing.py
│ │ │ └── utils.py
│ │ ├── CLAUDE.md
│ │ ├── docs
│ │ │ ├── design
│ │ │ │ ├── actions.md
│ │ │ │ └── inference.md
│ │ │ ├── DEV_GUIDE.md
│ │ │ ├── how-kta-works.md
│ │ │ ├── JTBD.md
│ │ │ ├── knowledge-transfer-goals.md
│ │ │ ├── learning_assistance.md
│ │ │ ├── notable_claude_conversations
│ │ │ │ ├── clarifying_quad_modal_design.md
│ │ │ │ ├── CLAUDE_PROMPTS.md
│ │ │ │ ├── transfer_state.md
│ │ │ │ └── trying_the_context_agent.md
│ │ │ └── opportunities-of-knowledge-transfer.md
│ │ ├── knowledge-transfer-assistant.code-workspace
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_artifact_loading.py
│ │ │ ├── test_inspector.py
│ │ │ ├── test_share_manager.py
│ │ │ ├── test_share_storage.py
│ │ │ ├── test_share_tools.py
│ │ │ └── test_team_mode.py
│ │ └── uv.lock
│ ├── Makefile
│ ├── navigator-assistant
│ │ ├── .env.example
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── assistant
│ │ │ ├── __init__.py
│ │ │ ├── assets
│ │ │ │ ├── card_content.md
│ │ │ │ └── icon.svg
│ │ │ ├── chat.py
│ │ │ ├── config.py
│ │ │ ├── helpers.py
│ │ │ ├── response
│ │ │ │ ├── __init__.py
│ │ │ │ ├── completion_handler.py
│ │ │ │ ├── completion_requestor.py
│ │ │ │ ├── local_tool
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── add_assistant_to_conversation.py
│ │ │ │ │ ├── list_assistant_services.py
│ │ │ │ │ └── model.py
│ │ │ │ ├── models.py
│ │ │ │ ├── prompt.py
│ │ │ │ ├── request_builder.py
│ │ │ │ ├── response.py
│ │ │ │ ├── step_handler.py
│ │ │ │ └── utils
│ │ │ │ ├── __init__.py
│ │ │ │ ├── formatting_utils.py
│ │ │ │ ├── message_utils.py
│ │ │ │ ├── openai_utils.py
│ │ │ │ └── tools.py
│ │ │ ├── text_includes
│ │ │ │ ├── guardrails_prompt.md
│ │ │ │ ├── guidance_prompt.md
│ │ │ │ ├── instruction_prompt.md
│ │ │ │ ├── navigator_assistant_info.md
│ │ │ │ └── semantic_workbench_features.md
│ │ │ └── whiteboard
│ │ │ ├── __init__.py
│ │ │ ├── _inspector.py
│ │ │ └── _whiteboard.py
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── project-assistant
│ │ ├── .cspell
│ │ │ └── custom-dictionary-workspace.txt
│ │ ├── .env.example
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── assistant
│ │ │ ├── __init__.py
│ │ │ ├── agentic
│ │ │ │ ├── __init__.py
│ │ │ │ ├── act.py
│ │ │ │ ├── coordinator_next_action.py
│ │ │ │ ├── create_invitation.py
│ │ │ │ ├── detect_audience_and_takeaways.py
│ │ │ │ ├── detect_coordinator_actions.py
│ │ │ │ ├── detect_information_request_needs.py
│ │ │ │ ├── detect_knowledge_package_gaps.py
│ │ │ │ ├── focus.py
│ │ │ │ ├── respond.py
│ │ │ │ ├── team_welcome.py
│ │ │ │ └── update_digest.py
│ │ │ ├── assets
│ │ │ │ ├── icon-knowledge-transfer.svg
│ │ │ │ └── icon.svg
│ │ │ ├── assistant.py
│ │ │ ├── common.py
│ │ │ ├── config.py
│ │ │ ├── conversation_clients.py
│ │ │ ├── data.py
│ │ │ ├── domain
│ │ │ │ ├── __init__.py
│ │ │ │ ├── audience_manager.py
│ │ │ │ ├── conversation_preferences_manager.py
│ │ │ │ ├── information_request_manager.py
│ │ │ │ ├── knowledge_brief_manager.py
│ │ │ │ ├── knowledge_digest_manager.py
│ │ │ │ ├── learning_objectives_manager.py
│ │ │ │ ├── share_manager.py
│ │ │ │ ├── tasks_manager.py
│ │ │ │ └── transfer_manager.py
│ │ │ ├── errors.py
│ │ │ ├── files.py
│ │ │ ├── logging.py
│ │ │ ├── notifications.py
│ │ │ ├── prompt_utils.py
│ │ │ ├── storage.py
│ │ │ ├── string_utils.py
│ │ │ ├── text_includes
│ │ │ │ ├── actor_instructions.md
│ │ │ │ ├── assistant_info.md
│ │ │ │ ├── card_content.md
│ │ │ │ ├── coordinator_instructions copy.md
│ │ │ │ ├── coordinator_instructions.md
│ │ │ │ ├── create_invitation.md
│ │ │ │ ├── detect_audience.md
│ │ │ │ ├── detect_coordinator_actions.md
│ │ │ │ ├── detect_information_request_needs.md
│ │ │ │ ├── detect_knowledge_package_gaps.md
│ │ │ │ ├── focus.md
│ │ │ │ ├── knowledge_digest_instructions.txt
│ │ │ │ ├── team_instructions.txt
│ │ │ │ ├── to_do.md
│ │ │ │ ├── update_knowledge_brief.md
│ │ │ │ ├── update_knowledge_digest.md
│ │ │ │ └── welcome_message_generation.txt
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── base.py
│ │ │ │ ├── conversation_preferences.py
│ │ │ │ ├── information_requests.py
│ │ │ │ ├── learning_objectives.py
│ │ │ │ ├── learning_outcomes.py
│ │ │ │ ├── progress_tracking.py
│ │ │ │ ├── share_setup.py
│ │ │ │ ├── system_reminders.py
│ │ │ │ ├── tasks.py
│ │ │ │ └── todo.py
│ │ │ ├── ui_tabs
│ │ │ │ ├── __init__.py
│ │ │ │ ├── brief.py
│ │ │ │ ├── common.py
│ │ │ │ ├── debug.py
│ │ │ │ ├── learning.py
│ │ │ │ └── sharing.py
│ │ │ └── utils.py
│ │ ├── CLAUDE.md
│ │ ├── docs
│ │ │ ├── design
│ │ │ │ ├── actions.md
│ │ │ │ ├── control_options.md
│ │ │ │ ├── design.md
│ │ │ │ ├── inference.md
│ │ │ │ └── PXL_20250814_190140267.jpg
│ │ │ ├── DEV_GUIDE.md
│ │ │ ├── how-kta-works.md
│ │ │ ├── JTBD.md
│ │ │ ├── knowledge-transfer-goals.md
│ │ │ ├── learning_assistance.md
│ │ │ ├── notable_claude_conversations
│ │ │ │ ├── clarifying_quad_modal_design.md
│ │ │ │ ├── CLAUDE_PROMPTS.md
│ │ │ │ ├── transfer_state.md
│ │ │ │ └── trying_the_context_agent.md
│ │ │ └── opportunities-of-knowledge-transfer.md
│ │ ├── knowledge-transfer-assistant.code-workspace
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── test_artifact_loading.py
│ │ │ ├── test_inspector.py
│ │ │ ├── test_share_manager.py
│ │ │ ├── test_share_storage.py
│ │ │ └── test_team_mode.py
│ │ └── uv.lock
│ ├── prospector-assistant
│ │ ├── .env.example
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── assistant
│ │ │ ├── __init__.py
│ │ │ ├── agents
│ │ │ │ ├── artifact_agent.py
│ │ │ │ ├── document
│ │ │ │ │ ├── config.py
│ │ │ │ │ ├── gc_draft_content_feedback_config.py
│ │ │ │ │ ├── gc_draft_outline_feedback_config.py
│ │ │ │ │ ├── guided_conversation.py
│ │ │ │ │ └── state.py
│ │ │ │ └── document_agent.py
│ │ │ ├── artifact_creation_extension
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _llm.py
│ │ │ │ ├── config.py
│ │ │ │ ├── document.py
│ │ │ │ ├── extension.py
│ │ │ │ ├── store.py
│ │ │ │ ├── test
│ │ │ │ │ ├── conftest.py
│ │ │ │ │ ├── evaluation.py
│ │ │ │ │ ├── test_completion_with_tools.py
│ │ │ │ │ └── test_extension.py
│ │ │ │ └── tools.py
│ │ │ ├── chat.py
│ │ │ ├── config.py
│ │ │ ├── form_fill_extension
│ │ │ │ ├── __init__.py
│ │ │ │ ├── config.py
│ │ │ │ ├── extension.py
│ │ │ │ ├── inspector.py
│ │ │ │ ├── state.py
│ │ │ │ └── steps
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _guided_conversation.py
│ │ │ │ ├── _llm.py
│ │ │ │ ├── acquire_form_step.py
│ │ │ │ ├── extract_form_fields_step.py
│ │ │ │ ├── fill_form_step.py
│ │ │ │ └── types.py
│ │ │ ├── helpers.py
│ │ │ ├── legacy.py
│ │ │ └── text_includes
│ │ │ ├── artifact_agent_enabled.md
│ │ │ ├── guardrails_prompt.txt
│ │ │ ├── guided_conversation_agent_enabled.md
│ │ │ └── skills_agent_enabled.md
│ │ ├── assistant.code-workspace
│ │ ├── gc_learnings
│ │ │ ├── gc_learnings.md
│ │ │ └── images
│ │ │ ├── gc_conversation_plan_fcn.png
│ │ │ ├── gc_conversation_plan_template.png
│ │ │ ├── gc_execute_plan_callstack.png
│ │ │ ├── gc_functions.png
│ │ │ ├── gc_generate_plan_callstack.png
│ │ │ ├── gc_get_resource_instructions.png
│ │ │ ├── gc_get_termination_instructions.png
│ │ │ ├── gc_kernel_arguments.png
│ │ │ ├── gc_plan_calls.png
│ │ │ ├── gc_termination_instructions.png
│ │ │ ├── sk_get_chat_message_contents.png
│ │ │ ├── sk_inner_get_chat_message_contents.png
│ │ │ ├── sk_send_request_prep.png
│ │ │ └── sk_send_request.png
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ └── skill-assistant
│ ├── .env.example
│ ├── .vscode
│ │ ├── launch.json
│ │ └── settings.json
│ ├── assistant
│ │ ├── __init__.py
│ │ ├── config.py
│ │ ├── logging.py
│ │ ├── skill_assistant.py
│ │ ├── skill_engine_registry.py
│ │ ├── skill_event_mapper.py
│ │ ├── text_includes
│ │ │ └── guardrails_prompt.txt
│ │ └── workbench_helpers.py
│ ├── assistant.code-workspace
│ ├── Makefile
│ ├── pyproject.toml
│ ├── README.md
│ ├── tests
│ │ └── test_setup.py
│ └── uv.lock
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── docs
│ ├── .vscode
│ │ └── settings.json
│ ├── ASSISTANT_CONFIG.md
│ ├── ASSISTANT_DEVELOPMENT_GUIDE.md
│ ├── CUSTOM_APP_REGISTRATION.md
│ ├── HOSTED_ASSISTANT_WITH_LOCAL_MCP_SERVERS.md
│ ├── images
│ │ ├── architecture-animation.gif
│ │ ├── configure_assistant.png
│ │ ├── conversation_canvas_open.png
│ │ ├── conversation_duplicate.png
│ │ ├── conversation_export.png
│ │ ├── conversation_share_dialog.png
│ │ ├── conversation_share_link.png
│ │ ├── dashboard_configured_view.png
│ │ ├── dashboard_view.png
│ │ ├── license_agreement.png
│ │ ├── message_bar.png
│ │ ├── message_inspection.png
│ │ ├── message_link.png
│ │ ├── new_prospector_assistant_dialog.png
│ │ ├── open_conversation_canvas.png
│ │ ├── prospector_example.png
│ │ ├── readme1.png
│ │ ├── readme2.png
│ │ ├── readme3.png
│ │ ├── rewind.png
│ │ ├── signin_page.png
│ │ └── splash_screen.png
│ ├── LOCAL_ASSISTANT_WITH_REMOTE_WORKBENCH.md
│ ├── SETUP_DEV_ENVIRONMENT.md
│ └── WORKBENCH_APP.md
├── examples
│ ├── dotnet
│ │ ├── .editorconfig
│ │ ├── dotnet-01-echo-bot
│ │ │ ├── appsettings.json
│ │ │ ├── dotnet-01-echo-bot.csproj
│ │ │ ├── MyAgent.cs
│ │ │ ├── MyAgentConfig.cs
│ │ │ ├── MyWorkbenchConnector.cs
│ │ │ ├── Program.cs
│ │ │ └── README.md
│ │ ├── dotnet-02-message-types-demo
│ │ │ ├── appsettings.json
│ │ │ ├── ConnectorExtensions.cs
│ │ │ ├── docs
│ │ │ │ ├── abc.png
│ │ │ │ ├── code.png
│ │ │ │ ├── config.png
│ │ │ │ ├── echo.png
│ │ │ │ ├── markdown.png
│ │ │ │ ├── mermaid.png
│ │ │ │ ├── reverse.png
│ │ │ │ └── safety-check.png
│ │ │ ├── dotnet-02-message-types-demo.csproj
│ │ │ ├── MyAgent.cs
│ │ │ ├── MyAgentConfig.cs
│ │ │ ├── MyWorkbenchConnector.cs
│ │ │ ├── Program.cs
│ │ │ └── README.md
│ │ └── dotnet-03-simple-chatbot
│ │ ├── appsettings.json
│ │ ├── ConnectorExtensions.cs
│ │ ├── dotnet-03-simple-chatbot.csproj
│ │ ├── MyAgent.cs
│ │ ├── MyAgentConfig.cs
│ │ ├── MyWorkbenchConnector.cs
│ │ ├── Program.cs
│ │ └── README.md
│ ├── Makefile
│ └── python
│ ├── python-01-echo-bot
│ │ ├── .env.example
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── assistant
│ │ │ ├── __init__.py
│ │ │ ├── chat.py
│ │ │ └── config.py
│ │ ├── assistant.code-workspace
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── python-02-simple-chatbot
│ │ ├── .env.example
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── assistant
│ │ │ ├── __init__.py
│ │ │ ├── chat.py
│ │ │ ├── config.py
│ │ │ └── text_includes
│ │ │ └── guardrails_prompt.txt
│ │ ├── assistant.code-workspace
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ └── python-03-multimodel-chatbot
│ ├── .env.example
│ ├── .vscode
│ │ ├── launch.json
│ │ └── settings.json
│ ├── assistant
│ │ ├── __init__.py
│ │ ├── chat.py
│ │ ├── config.py
│ │ ├── model_adapters.py
│ │ └── text_includes
│ │ └── guardrails_prompt.txt
│ ├── assistant.code-workspace
│ ├── Makefile
│ ├── pyproject.toml
│ ├── README.md
│ └── uv.lock
├── KNOWN_ISSUES.md
├── libraries
│ ├── dotnet
│ │ ├── .editorconfig
│ │ ├── pack.sh
│ │ ├── README.md
│ │ ├── SemanticWorkbench.sln
│ │ ├── SemanticWorkbench.sln.DotSettings
│ │ └── WorkbenchConnector
│ │ ├── AgentBase.cs
│ │ ├── AgentConfig
│ │ │ ├── AgentConfigBase.cs
│ │ │ ├── AgentConfigPropertyAttribute.cs
│ │ │ └── ConfigUtils.cs
│ │ ├── Constants.cs
│ │ ├── IAgentBase.cs
│ │ ├── icon.png
│ │ ├── Models
│ │ │ ├── Command.cs
│ │ │ ├── Conversation.cs
│ │ │ ├── ConversationEvent.cs
│ │ │ ├── DebugInfo.cs
│ │ │ ├── Insight.cs
│ │ │ ├── Message.cs
│ │ │ ├── MessageMetadata.cs
│ │ │ ├── Participant.cs
│ │ │ ├── Sender.cs
│ │ │ └── ServiceInfo.cs
│ │ ├── Storage
│ │ │ ├── AgentInfo.cs
│ │ │ ├── AgentServiceStorage.cs
│ │ │ └── IAgentServiceStorage.cs
│ │ ├── StringLoggingExtensions.cs
│ │ ├── Webservice.cs
│ │ ├── WorkbenchConfig.cs
│ │ ├── WorkbenchConnector.cs
│ │ └── WorkbenchConnector.csproj
│ ├── Makefile
│ └── python
│ ├── anthropic-client
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── anthropic_client
│ │ │ ├── __init__.py
│ │ │ ├── client.py
│ │ │ ├── config.py
│ │ │ └── messages.py
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── assistant-data-gen
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── assistant_data_gen
│ │ │ ├── __init__.py
│ │ │ ├── assistant_api.py
│ │ │ ├── config.py
│ │ │ ├── gce
│ │ │ │ ├── __init__.py
│ │ │ │ ├── gce_agent.py
│ │ │ │ └── prompts.py
│ │ │ └── pydantic_ai_utils.py
│ │ ├── configs
│ │ │ └── document_assistant_example_config.yaml
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── scripts
│ │ │ ├── gce_simulation.py
│ │ │ └── generate_scenario.py
│ │ └── uv.lock
│ ├── assistant-drive
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ ├── extensions.json
│ │ │ └── settings.json
│ │ ├── assistant_drive
│ │ │ ├── __init__.py
│ │ │ ├── drive.py
│ │ │ └── tests
│ │ │ └── test_basic.py
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── pytest.ini
│ │ ├── README.md
│ │ ├── usage.ipynb
│ │ └── uv.lock
│ ├── assistant-extensions
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── assistant_extensions
│ │ │ ├── __init__.py
│ │ │ ├── ai_clients
│ │ │ │ └── config.py
│ │ │ ├── artifacts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _artifacts.py
│ │ │ │ ├── _inspector.py
│ │ │ │ └── _model.py
│ │ │ ├── attachments
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _attachments.py
│ │ │ │ ├── _convert.py
│ │ │ │ ├── _model.py
│ │ │ │ ├── _shared.py
│ │ │ │ └── _summarizer.py
│ │ │ ├── chat_context_toolkit
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _config.py
│ │ │ │ ├── archive
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── _archive.py
│ │ │ │ │ └── _summarizer.py
│ │ │ │ ├── message_history
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── _history.py
│ │ │ │ │ └── _message.py
│ │ │ │ └── virtual_filesystem
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _archive_file_source.py
│ │ │ │ └── _attachments_file_source.py
│ │ │ ├── dashboard_card
│ │ │ │ ├── __init__.py
│ │ │ │ └── _dashboard_card.py
│ │ │ ├── document_editor
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _extension.py
│ │ │ │ ├── _inspector.py
│ │ │ │ └── _model.py
│ │ │ ├── mcp
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _assistant_file_resource_handler.py
│ │ │ │ ├── _client_utils.py
│ │ │ │ ├── _devtunnel.py
│ │ │ │ ├── _model.py
│ │ │ │ ├── _openai_utils.py
│ │ │ │ ├── _sampling_handler.py
│ │ │ │ ├── _tool_utils.py
│ │ │ │ └── _workbench_file_resource_handler.py
│ │ │ ├── navigator
│ │ │ │ ├── __init__.py
│ │ │ │ └── _navigator.py
│ │ │ └── workflows
│ │ │ ├── __init__.py
│ │ │ ├── _model.py
│ │ │ ├── _workflows.py
│ │ │ └── runners
│ │ │ └── _user_proxy.py
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── test
│ │ │ └── attachments
│ │ │ └── test_attachments.py
│ │ └── uv.lock
│ ├── chat-context-toolkit
│ │ ├── .claude
│ │ │ └── settings.local.json
│ │ ├── .env.sample
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── assets
│ │ │ ├── archive_v1.png
│ │ │ ├── history_v1.png
│ │ │ └── vfs_v1.png
│ │ ├── chat_context_toolkit
│ │ │ ├── __init__.py
│ │ │ ├── archive
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _archive_reader.py
│ │ │ │ ├── _archive_task_queue.py
│ │ │ │ ├── _state.py
│ │ │ │ ├── _types.py
│ │ │ │ └── summarization
│ │ │ │ ├── __init__.py
│ │ │ │ └── _summarizer.py
│ │ │ ├── history
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _budget.py
│ │ │ │ ├── _decorators.py
│ │ │ │ ├── _history.py
│ │ │ │ ├── _prioritize.py
│ │ │ │ ├── _types.py
│ │ │ │ └── tool_abbreviations
│ │ │ │ ├── __init__.py
│ │ │ │ └── _tool_abbreviations.py
│ │ │ └── virtual_filesystem
│ │ │ ├── __init__.py
│ │ │ ├── _types.py
│ │ │ ├── _virtual_filesystem.py
│ │ │ ├── README.md
│ │ │ └── tools
│ │ │ ├── __init__.py
│ │ │ ├── _ls_tool.py
│ │ │ ├── _tools.py
│ │ │ └── _view_tool.py
│ │ ├── CLAUDE.md
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── test
│ │ │ ├── archive
│ │ │ │ └── test_archive_reader.py
│ │ │ ├── history
│ │ │ │ ├── test_abbreviate_messages.py
│ │ │ │ ├── test_history.py
│ │ │ │ ├── test_pair_and_order_tool_messages.py
│ │ │ │ ├── test_prioritize.py
│ │ │ │ └── test_truncate_messages.py
│ │ │ └── virtual_filesystem
│ │ │ ├── test_virtual_filesystem.py
│ │ │ └── tools
│ │ │ ├── test_ls_tool.py
│ │ │ ├── test_tools.py
│ │ │ └── test_view_tool.py
│ │ └── uv.lock
│ ├── content-safety
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── content_safety
│ │ │ ├── __init__.py
│ │ │ ├── evaluators
│ │ │ │ ├── __init__.py
│ │ │ │ ├── azure_content_safety
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── config.py
│ │ │ │ │ └── evaluator.py
│ │ │ │ ├── config.py
│ │ │ │ ├── evaluator.py
│ │ │ │ └── openai_moderations
│ │ │ │ ├── __init__.py
│ │ │ │ ├── config.py
│ │ │ │ └── evaluator.py
│ │ │ └── README.md
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── events
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── events
│ │ │ ├── __init__.py
│ │ │ └── events.py
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── guided-conversation
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── guided_conversation
│ │ │ ├── __init__.py
│ │ │ ├── functions
│ │ │ │ ├── __init__.py
│ │ │ │ ├── conversation_plan.py
│ │ │ │ ├── execution.py
│ │ │ │ └── final_update_plan.py
│ │ │ ├── guided_conversation_agent.py
│ │ │ ├── plugins
│ │ │ │ ├── __init__.py
│ │ │ │ ├── agenda.py
│ │ │ │ └── artifact.py
│ │ │ └── utils
│ │ │ ├── __init__.py
│ │ │ ├── base_model_llm.py
│ │ │ ├── conversation_helpers.py
│ │ │ ├── openai_tool_calling.py
│ │ │ ├── plugin_helpers.py
│ │ │ └── resources.py
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── llm-client
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── llm_client
│ │ │ ├── __init__.py
│ │ │ └── model.py
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── Makefile
│ ├── mcp-extensions
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── mcp_extensions
│ │ │ ├── __init__.py
│ │ │ ├── _client_session.py
│ │ │ ├── _model.py
│ │ │ ├── _sampling.py
│ │ │ ├── _server_extensions.py
│ │ │ ├── _tool_utils.py
│ │ │ ├── llm
│ │ │ │ ├── __init__.py
│ │ │ │ ├── chat_completion.py
│ │ │ │ ├── helpers.py
│ │ │ │ ├── llm_types.py
│ │ │ │ ├── mcp_chat_completion.py
│ │ │ │ └── openai_chat_completion.py
│ │ │ └── server
│ │ │ ├── __init__.py
│ │ │ └── storage.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ └── test_tool_utils.py
│ │ └── uv.lock
│ ├── mcp-tunnel
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── mcp_tunnel
│ │ │ ├── __init__.py
│ │ │ ├── _devtunnel.py
│ │ │ ├── _dir.py
│ │ │ └── _main.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── openai-client
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── openai_client
│ │ │ ├── __init__.py
│ │ │ ├── chat_driver
│ │ │ │ ├── __init__.py
│ │ │ │ ├── chat_driver.ipynb
│ │ │ │ ├── chat_driver.py
│ │ │ │ ├── message_history_providers
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── in_memory_message_history_provider.py
│ │ │ │ │ ├── local_message_history_provider.py
│ │ │ │ │ ├── message_history_provider.py
│ │ │ │ │ └── tests
│ │ │ │ │ └── formatted_instructions_test.py
│ │ │ │ └── README.md
│ │ │ ├── client.py
│ │ │ ├── completion.py
│ │ │ ├── config.py
│ │ │ ├── errors.py
│ │ │ ├── logging.py
│ │ │ ├── messages.py
│ │ │ ├── tokens.py
│ │ │ └── tools.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── test_command_parsing.py
│ │ │ ├── test_formatted_messages.py
│ │ │ ├── test_messages.py
│ │ │ └── test_tokens.py
│ │ └── uv.lock
│ ├── semantic-workbench-api-model
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── semantic_workbench_api_model
│ │ │ ├── __init__.py
│ │ │ ├── assistant_model.py
│ │ │ ├── assistant_service_client.py
│ │ │ ├── workbench_model.py
│ │ │ └── workbench_service_client.py
│ │ └── uv.lock
│ ├── semantic-workbench-assistant
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── semantic_workbench_assistant
│ │ │ ├── __init__.py
│ │ │ ├── assistant_app
│ │ │ │ ├── __init__.py
│ │ │ │ ├── assistant.py
│ │ │ │ ├── config.py
│ │ │ │ ├── content_safety.py
│ │ │ │ ├── context.py
│ │ │ │ ├── error.py
│ │ │ │ ├── export_import.py
│ │ │ │ ├── protocol.py
│ │ │ │ └── service.py
│ │ │ ├── assistant_service.py
│ │ │ ├── auth.py
│ │ │ ├── canonical.py
│ │ │ ├── command.py
│ │ │ ├── config.py
│ │ │ ├── logging_config.py
│ │ │ ├── settings.py
│ │ │ ├── start.py
│ │ │ └── storage.py
│ │ ├── tests
│ │ │ ├── conftest.py
│ │ │ ├── test_assistant_app.py
│ │ │ ├── test_canonical.py
│ │ │ ├── test_config.py
│ │ │ └── test_storage.py
│ │ └── uv.lock
│ └── skills
│ ├── .vscode
│ │ └── settings.json
│ ├── Makefile
│ ├── README.md
│ └── skill-library
│ ├── .vscode
│ │ └── settings.json
│ ├── docs
│ │ └── vs-recipe-tool.md
│ ├── Makefile
│ ├── pyproject.toml
│ ├── README.md
│ ├── skill_library
│ │ ├── __init__.py
│ │ ├── chat_driver_helpers.py
│ │ ├── cli
│ │ │ ├── azure_openai.py
│ │ │ ├── conversation_history.py
│ │ │ ├── README.md
│ │ │ ├── run_routine.py
│ │ │ ├── settings.py
│ │ │ └── skill_logger.py
│ │ ├── engine.py
│ │ ├── llm_info.txt
│ │ ├── logging.py
│ │ ├── README.md
│ │ ├── routine_stack.py
│ │ ├── skill.py
│ │ ├── skills
│ │ │ ├── common
│ │ │ │ ├── __init__.py
│ │ │ │ ├── common_skill.py
│ │ │ │ └── routines
│ │ │ │ ├── bing_search.py
│ │ │ │ ├── consolidate.py
│ │ │ │ ├── echo.py
│ │ │ │ ├── gather_context.py
│ │ │ │ ├── get_content_from_url.py
│ │ │ │ ├── gpt_complete.py
│ │ │ │ ├── select_user_intent.py
│ │ │ │ └── summarize.py
│ │ │ ├── eval
│ │ │ │ ├── __init__.py
│ │ │ │ ├── eval_skill.py
│ │ │ │ └── routines
│ │ │ │ └── eval.py
│ │ │ ├── fabric
│ │ │ │ ├── __init__.py
│ │ │ │ ├── fabric_skill.py
│ │ │ │ ├── patterns
│ │ │ │ │ ├── agility_story
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── ai
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_answers
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_candidates
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── analyze_cfp_submission
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_claims
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── analyze_comments
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_debate
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_email_headers
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── analyze_incident
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── analyze_interviewer_techniques
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_logs
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_malware
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_military_strategy
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_mistakes
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_paper
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── analyze_patent
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_personality
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_presentation
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_product_feedback
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_proposition
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── analyze_prose
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── analyze_prose_json
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── analyze_prose_pinker
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_risk
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_sales_call
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_spiritual_text
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── analyze_tech_impact
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── analyze_threat_report
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── analyze_threat_report_cmds
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── analyze_threat_report_trends
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── answer_interview_question
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── ask_secure_by_design_questions
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── ask_uncle_duke
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── capture_thinkers_work
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── check_agreement
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── clean_text
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── coding_master
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── compare_and_contrast
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── convert_to_markdown
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_5_sentence_summary
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_academic_paper
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_ai_jobs_analysis
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_aphorisms
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── create_art_prompt
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_better_frame
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── create_coding_project
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_command
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── create_cyber_summary
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_design_document
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_diy
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_formal_email
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_git_diff_commit
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_graph_from_input
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_hormozi_offer
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_idea_compass
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_investigation_visualization
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_keynote
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_logo
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── create_markmap_visualization
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_mermaid_visualization
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_mermaid_visualization_for_github
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_micro_summary
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_network_threat_landscape
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── create_newsletter_entry
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── create_npc
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── create_pattern
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_prd
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_prediction_block
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_quiz
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_reading_plan
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_recursive_outline
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_report_finding
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── create_rpg_summary
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_security_update
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── create_show_intro
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_sigma_rules
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_story_explanation
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_stride_threat_model
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_summary
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_tags
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_threat_scenarios
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_ttrc_graph
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_ttrc_narrative
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_upgrade_pack
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_user_story
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── create_video_chapters
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── create_visualization
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── dialog_with_socrates
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── enrich_blog_post
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── explain_code
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── explain_docs
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── explain_math
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── explain_project
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── explain_terms
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── export_data_as_csv
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_algorithm_update_recommendations
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── extract_article_wisdom
│ │ │ │ │ │ ├── dmiessler
│ │ │ │ │ │ │ └── extract_wisdom-1.0.0
│ │ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ │ └── user.md
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── extract_book_ideas
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_book_recommendations
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_business_ideas
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_controversial_ideas
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_core_message
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_ctf_writeup
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_domains
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_extraordinary_claims
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_ideas
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_insights
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_insights_dm
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_instructions
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_jokes
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_latest_video
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_main_idea
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_most_redeeming_thing
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_patterns
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_poc
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── extract_predictions
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_primary_problem
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_primary_solution
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_product_features
│ │ │ │ │ │ ├── dmiessler
│ │ │ │ │ │ │ └── extract_wisdom-1.0.0
│ │ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ │ └── user.md
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_questions
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_recipe
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_recommendations
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── extract_references
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── extract_skills
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_song_meaning
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_sponsors
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_videoid
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── extract_wisdom
│ │ │ │ │ │ ├── dmiessler
│ │ │ │ │ │ │ └── extract_wisdom-1.0.0
│ │ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ │ └── user.md
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_wisdom_agents
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_wisdom_dm
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── extract_wisdom_nometa
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── find_hidden_message
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── find_logical_fallacies
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── get_wow_per_minute
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── get_youtube_rss
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── humanize
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── identify_dsrp_distinctions
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── identify_dsrp_perspectives
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── identify_dsrp_relationships
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── identify_dsrp_systems
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── identify_job_stories
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── improve_academic_writing
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── improve_prompt
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── improve_report_finding
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── improve_writing
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── judge_output
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── label_and_rate
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── loaded
│ │ │ │ │ ├── md_callout
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── official_pattern_template
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── pattern_explanations.md
│ │ │ │ │ ├── prepare_7s_strategy
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── provide_guidance
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── rate_ai_response
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── rate_ai_result
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── rate_content
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── rate_value
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── raw_query
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── raycast
│ │ │ │ │ │ ├── capture_thinkers_work
│ │ │ │ │ │ ├── create_story_explanation
│ │ │ │ │ │ ├── extract_primary_problem
│ │ │ │ │ │ ├── extract_wisdom
│ │ │ │ │ │ └── yt
│ │ │ │ │ ├── recommend_artists
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── recommend_pipeline_upgrades
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── recommend_talkpanel_topics
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── refine_design_document
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── review_design
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── sanitize_broken_html_to_markdown
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── show_fabric_options_markmap
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── solve_with_cot
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── stringify
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── suggest_pattern
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── summarize
│ │ │ │ │ │ ├── dmiessler
│ │ │ │ │ │ │ └── summarize
│ │ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ │ └── user.md
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── summarize_debate
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── summarize_git_changes
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── summarize_git_diff
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── summarize_lecture
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── summarize_legislation
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── summarize_meeting
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── summarize_micro
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── summarize_newsletter
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── summarize_paper
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── summarize_prompt
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── summarize_pull-requests
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── summarize_rpg_session
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_analyze_challenge_handling
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_check_metrics
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_create_h3_career
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_create_opening_sentences
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_describe_life_outlook
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_extract_intro_sentences
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_extract_panel_topics
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_find_blindspots
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_find_negative_thinking
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_find_neglected_goals
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_give_encouragement
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_red_team_thinking
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_threat_model_plans
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_visualize_mission_goals_projects
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── t_year_in_review
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── to_flashcards
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── transcribe_minutes
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── translate
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── tweet
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── write_essay
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── write_hackerone_report
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── write_latex
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── write_micro_essay
│ │ │ │ │ │ └── system.md
│ │ │ │ │ ├── write_nuclei_template_rule
│ │ │ │ │ │ ├── system.md
│ │ │ │ │ │ └── user.md
│ │ │ │ │ ├── write_pull-request
│ │ │ │ │ │ └── system.md
│ │ │ │ │ └── write_semgrep_rule
│ │ │ │ │ ├── system.md
│ │ │ │ │ └── user.md
│ │ │ │ └── routines
│ │ │ │ ├── list.py
│ │ │ │ ├── run.py
│ │ │ │ └── show.py
│ │ │ ├── guided_conversation
│ │ │ │ ├── __init__.py
│ │ │ │ ├── agenda.py
│ │ │ │ ├── artifact_helpers.py
│ │ │ │ ├── chat_completions
│ │ │ │ │ ├── fix_agenda_error.py
│ │ │ │ │ ├── fix_artifact_error.py
│ │ │ │ │ ├── generate_agenda.py
│ │ │ │ │ ├── generate_artifact_updates.py
│ │ │ │ │ ├── generate_final_artifact.py
│ │ │ │ │ └── generate_message.py
│ │ │ │ ├── conversation_guides
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── acrostic_poem.py
│ │ │ │ │ ├── er_triage.py
│ │ │ │ │ ├── interview.py
│ │ │ │ │ └── patient_intake.py
│ │ │ │ ├── guide.py
│ │ │ │ ├── guided_conversation_skill.py
│ │ │ │ ├── logging.py
│ │ │ │ ├── message.py
│ │ │ │ ├── resources.py
│ │ │ │ ├── routines
│ │ │ │ │ └── guided_conversation.py
│ │ │ │ └── tests
│ │ │ │ ├── conftest.py
│ │ │ │ ├── test_artifact_helpers.py
│ │ │ │ ├── test_generate_agenda.py
│ │ │ │ ├── test_generate_artifact_updates.py
│ │ │ │ ├── test_generate_final_artifact.py
│ │ │ │ └── test_resource.py
│ │ │ ├── meta
│ │ │ │ ├── __init__.py
│ │ │ │ ├── meta_skill.py
│ │ │ │ ├── README.md
│ │ │ │ └── routines
│ │ │ │ └── generate_routine.py
│ │ │ ├── posix
│ │ │ │ ├── __init__.py
│ │ │ │ ├── posix_skill.py
│ │ │ │ ├── routines
│ │ │ │ │ ├── append_file.py
│ │ │ │ │ ├── cd.py
│ │ │ │ │ ├── ls.py
│ │ │ │ │ ├── make_home_dir.py
│ │ │ │ │ ├── mkdir.py
│ │ │ │ │ ├── mv.py
│ │ │ │ │ ├── pwd.py
│ │ │ │ │ ├── read_file.py
│ │ │ │ │ ├── rm.py
│ │ │ │ │ ├── touch.py
│ │ │ │ │ └── write_file.py
│ │ │ │ └── sandbox_shell.py
│ │ │ ├── README.md
│ │ │ ├── research
│ │ │ │ ├── __init__.py
│ │ │ │ ├── README.md
│ │ │ │ ├── research_skill.py
│ │ │ │ └── routines
│ │ │ │ ├── answer_question_about_content.py
│ │ │ │ ├── evaluate_answer.py
│ │ │ │ ├── generate_research_plan.py
│ │ │ │ ├── generate_search_query.py
│ │ │ │ ├── update_research_plan.py
│ │ │ │ ├── web_research.py
│ │ │ │ └── web_search.py
│ │ │ ├── research2
│ │ │ │ ├── __init__.py
│ │ │ │ ├── README.md
│ │ │ │ ├── research_skill.py
│ │ │ │ └── routines
│ │ │ │ ├── facts.py
│ │ │ │ ├── make_final_report.py
│ │ │ │ ├── research.py
│ │ │ │ ├── search_plan.py
│ │ │ │ ├── search.py
│ │ │ │ └── visit_pages.py
│ │ │ └── web_research
│ │ │ ├── __init__.py
│ │ │ ├── README.md
│ │ │ ├── research_skill.py
│ │ │ └── routines
│ │ │ ├── facts.py
│ │ │ ├── make_final_report.py
│ │ │ ├── research.py
│ │ │ ├── search_plan.py
│ │ │ ├── search.py
│ │ │ └── visit_pages.py
│ │ ├── tests
│ │ │ ├── test_common_skill.py
│ │ │ ├── test_integration.py
│ │ │ ├── test_routine_stack.py
│ │ │ ├── tst_skill
│ │ │ │ ├── __init__.py
│ │ │ │ └── routines
│ │ │ │ ├── __init__.py
│ │ │ │ └── a_routine.py
│ │ │ └── utilities
│ │ │ ├── test_find_template_vars.py
│ │ │ ├── test_make_arg_set.py
│ │ │ ├── test_paramspec.py
│ │ │ ├── test_parse_command_string.py
│ │ │ └── test_to_string.py
│ │ ├── types.py
│ │ ├── usage.py
│ │ └── utilities.py
│ └── uv.lock
├── LICENSE
├── Makefile
├── mcp-servers
│ ├── ai-assist-content
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── mcp-example-brave-search.md
│ │ ├── mcp-fastmcp-typescript-README.md
│ │ ├── mcp-llms-full.txt
│ │ ├── mcp-metadata-tips.md
│ │ ├── mcp-python-sdk-README.md
│ │ ├── mcp-typescript-sdk-README.md
│ │ ├── pydanticai-documentation.md
│ │ ├── pydanticai-example-question-graph.md
│ │ ├── pydanticai-example-weather.md
│ │ ├── pydanticai-tutorial.md
│ │ └── README.md
│ ├── Makefile
│ ├── mcp-server-bing-search
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── mcp_server_bing_search
│ │ │ ├── __init__.py
│ │ │ ├── config.py
│ │ │ ├── prompts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── clean_website.py
│ │ │ │ └── filter_links.py
│ │ │ ├── server.py
│ │ │ ├── start.py
│ │ │ ├── tools.py
│ │ │ ├── types.py
│ │ │ ├── utils.py
│ │ │ └── web
│ │ │ ├── __init__.py
│ │ │ ├── get_content.py
│ │ │ ├── llm_processing.py
│ │ │ ├── process_website.py
│ │ │ └── search_bing.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ └── test_tools.py
│ │ └── uv.lock
│ ├── mcp-server-bundle
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── mcp_server_bundle
│ │ │ ├── __init__.py
│ │ │ └── main.py
│ │ ├── pyinstaller.spec
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── mcp-server-filesystem
│ │ ├── .env.example
│ │ ├── .github
│ │ │ └── workflows
│ │ │ └── ci.yml
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── mcp_server_filesystem
│ │ │ ├── __init__.py
│ │ │ ├── config.py
│ │ │ ├── server.py
│ │ │ └── start.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ └── test_filesystem.py
│ │ └── uv.lock
│ ├── mcp-server-filesystem-edit
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── data
│ │ │ ├── attachments
│ │ │ │ ├── Daily Game Ideas.txt
│ │ │ │ ├── Frontend Framework Proposal.txt
│ │ │ │ ├── ReDoodle.txt
│ │ │ │ └── Research Template.tex
│ │ │ ├── test_cases.yaml
│ │ │ └── transcripts
│ │ │ ├── transcript_research_simple.md
│ │ │ ├── transcript_Startup_Idea_1_202503031513.md
│ │ │ ├── transcript_Startup_Idea_2_202503031659.md
│ │ │ └── transcript_Web_Frontends_202502281551.md
│ │ ├── Makefile
│ │ ├── mcp_server_filesystem_edit
│ │ │ ├── __init__.py
│ │ │ ├── app_handling
│ │ │ │ ├── __init__.py
│ │ │ │ ├── excel.py
│ │ │ │ ├── miktex.py
│ │ │ │ ├── office_common.py
│ │ │ │ ├── powerpoint.py
│ │ │ │ └── word.py
│ │ │ ├── config.py
│ │ │ ├── evals
│ │ │ │ ├── __init__.py
│ │ │ │ ├── common.py
│ │ │ │ ├── run_comments.py
│ │ │ │ ├── run_edit.py
│ │ │ │ └── run_ppt_edit.py
│ │ │ ├── prompts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── add_comments.py
│ │ │ │ ├── analyze_comments.py
│ │ │ │ ├── latex_edit.py
│ │ │ │ ├── markdown_draft.py
│ │ │ │ ├── markdown_edit.py
│ │ │ │ └── powerpoint_edit.py
│ │ │ ├── server.py
│ │ │ ├── start.py
│ │ │ ├── tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── add_comments.py
│ │ │ │ ├── edit_adapters
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── common.py
│ │ │ │ │ ├── latex.py
│ │ │ │ │ └── markdown.py
│ │ │ │ ├── edit.py
│ │ │ │ └── helpers.py
│ │ │ └── types.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ ├── app_handling
│ │ │ │ ├── test_excel.py
│ │ │ │ ├── test_miktext.py
│ │ │ │ ├── test_office_common.py
│ │ │ │ ├── test_powerpoint.py
│ │ │ │ └── test_word.py
│ │ │ ├── conftest.py
│ │ │ └── tools
│ │ │ └── edit_adapters
│ │ │ ├── test_latex.py
│ │ │ └── test_markdown.py
│ │ └── uv.lock
│ ├── mcp-server-fusion
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── AddInIcon.svg
│ │ ├── config.py
│ │ ├── FusionMCPServerAddIn.manifest
│ │ ├── FusionMCPServerAddIn.py
│ │ ├── mcp_server_fusion
│ │ │ ├── __init__.py
│ │ │ ├── fusion_mcp_server.py
│ │ │ ├── fusion_utils
│ │ │ │ ├── __init__.py
│ │ │ │ ├── event_utils.py
│ │ │ │ ├── general_utils.py
│ │ │ │ └── tool_utils.py
│ │ │ ├── mcp_tools
│ │ │ │ ├── __init__.py
│ │ │ │ ├── fusion_3d_operation.py
│ │ │ │ ├── fusion_geometry.py
│ │ │ │ ├── fusion_pattern.py
│ │ │ │ └── fusion_sketch.py
│ │ │ └── vendor
│ │ │ └── README.md
│ │ ├── README.md
│ │ └── requirements.txt
│ ├── mcp-server-giphy
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── config.py
│ │ │ ├── giphy_search.py
│ │ │ ├── sampling.py
│ │ │ ├── server.py
│ │ │ ├── start.py
│ │ │ └── utils.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── mcp-server-memory-user-bio
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── mcp_server_memory_user_bio
│ │ │ ├── __init__.py
│ │ │ ├── config.py
│ │ │ ├── server.py
│ │ │ └── start.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── mcp-server-memory-whiteboard
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── mcp_server_memory_whiteboard
│ │ │ ├── __init__.py
│ │ │ ├── config.py
│ │ │ ├── server.py
│ │ │ └── start.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── mcp-server-office
│ │ ├── .env.example
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── build.sh
│ │ ├── data
│ │ │ ├── attachments
│ │ │ │ ├── Daily Game Ideas.txt
│ │ │ │ ├── Frontend Framework Proposal.txt
│ │ │ │ └── ReDoodle.txt
│ │ │ └── word
│ │ │ ├── test_cases.yaml
│ │ │ └── transcripts
│ │ │ ├── transcript_Startup_Idea_1_202503031513.md
│ │ │ ├── transcript_Startup_Idea_2_202503031659.md
│ │ │ └── transcript_Web_Frontends_202502281551.md
│ │ ├── Makefile
│ │ ├── mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── app_interaction
│ │ │ │ ├── __init__.py
│ │ │ │ ├── excel_editor.py
│ │ │ │ ├── powerpoint_editor.py
│ │ │ │ └── word_editor.py
│ │ │ ├── config.py
│ │ │ ├── constants.py
│ │ │ ├── evals
│ │ │ │ ├── __init__.py
│ │ │ │ ├── common.py
│ │ │ │ ├── run_comment_analysis.py
│ │ │ │ ├── run_feedback.py
│ │ │ │ └── run_markdown_edit.py
│ │ │ ├── helpers.py
│ │ │ ├── markdown_edit
│ │ │ │ ├── __init__.py
│ │ │ │ ├── comment_analysis.py
│ │ │ │ ├── feedback_step.py
│ │ │ │ ├── markdown_edit.py
│ │ │ │ └── utils.py
│ │ │ ├── prompts
│ │ │ │ ├── __init__.py
│ │ │ │ ├── comment_analysis.py
│ │ │ │ ├── feedback.py
│ │ │ │ ├── markdown_draft.py
│ │ │ │ └── markdown_edit.py
│ │ │ ├── server.py
│ │ │ ├── start.py
│ │ │ └── types.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── tests
│ │ │ └── test_word_editor.py
│ │ └── uv.lock
│ ├── mcp-server-open-deep-research
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── mcp_server
│ │ │ ├── __init__.py
│ │ │ ├── config.py
│ │ │ ├── libs
│ │ │ │ └── open_deep_research
│ │ │ │ ├── cookies.py
│ │ │ │ ├── mdconvert.py
│ │ │ │ ├── run_agents.py
│ │ │ │ ├── text_inspector_tool.py
│ │ │ │ ├── text_web_browser.py
│ │ │ │ └── visual_qa.py
│ │ │ ├── open_deep_research.py
│ │ │ ├── server.py
│ │ │ └── start.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ └── uv.lock
│ ├── mcp-server-open-deep-research-clone
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ ├── launch.json
│ │ │ └── settings.json
│ │ ├── Makefile
│ │ ├── mcp_server_open_deep_research_clone
│ │ │ ├── __init__.py
│ │ │ ├── azure_openai.py
│ │ │ ├── config.py
│ │ │ ├── logging.py
│ │ │ ├── sampling.py
│ │ │ ├── server.py
│ │ │ ├── start.py
│ │ │ ├── utils.py
│ │ │ └── web_research.py
│ │ ├── pyproject.toml
│ │ ├── README.md
│ │ ├── test
│ │ │ └── test_open_deep_research_clone.py
│ │ └── uv.lock
│ ├── mcp-server-template
│ │ ├── .taplo.toml
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── copier.yml
│ │ ├── README.md
│ │ └── template
│ │ └── {{ project_slug }}
│ │ ├── .env.example.jinja
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ ├── launch.json.jinja
│ │ │ └── settings.json
│ │ ├── {{ module_name }}
│ │ │ ├── __init__.py
│ │ │ ├── config.py.jinja
│ │ │ ├── server.py.jinja
│ │ │ └── start.py.jinja
│ │ ├── Makefile.jinja
│ │ ├── pyproject.toml.jinja
│ │ └── README.md.jinja
│ ├── mcp-server-vscode
│ │ ├── .eslintrc.cjs
│ │ ├── .gitignore
│ │ ├── .npmrc
│ │ ├── .vscode
│ │ │ ├── extensions.json
│ │ │ ├── launch.json
│ │ │ ├── settings.json
│ │ │ └── tasks.json
│ │ ├── .vscode-test.mjs
│ │ ├── .vscodeignore
│ │ ├── ASSISTANT_BOOTSTRAP.md
│ │ ├── eslint.config.mjs
│ │ ├── images
│ │ │ └── icon.png
│ │ ├── LICENSE
│ │ ├── Makefile
│ │ ├── out
│ │ │ ├── extension.d.ts
│ │ │ ├── extension.js
│ │ │ ├── test
│ │ │ │ ├── extension.test.d.ts
│ │ │ │ └── extension.test.js
│ │ │ ├── tools
│ │ │ │ ├── code_checker.d.ts
│ │ │ │ ├── code_checker.js
│ │ │ │ ├── debug_tools.d.ts
│ │ │ │ ├── debug_tools.js
│ │ │ │ ├── focus_editor.d.ts
│ │ │ │ ├── focus_editor.js
│ │ │ │ ├── search_symbol.d.ts
│ │ │ │ └── search_symbol.js
│ │ │ └── utils
│ │ │ ├── port.d.ts
│ │ │ └── port.js
│ │ ├── package.json
│ │ ├── pnpm-lock.yaml
│ │ ├── prettier.config.cjs
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── extension.d.ts
│ │ │ ├── extension.ts
│ │ │ ├── test
│ │ │ │ ├── extension.test.d.ts
│ │ │ │ └── extension.test.ts
│ │ │ ├── tools
│ │ │ │ ├── code_checker.d.ts
│ │ │ │ ├── code_checker.ts
│ │ │ │ ├── debug_tools.d.ts
│ │ │ │ ├── debug_tools.ts
│ │ │ │ ├── focus_editor.d.ts
│ │ │ │ ├── focus_editor.ts
│ │ │ │ ├── search_symbol.d.ts
│ │ │ │ └── search_symbol.ts
│ │ │ └── utils
│ │ │ ├── port.d.ts
│ │ │ └── port.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.tsbuildinfo
│ │ ├── vsc-extension-quickstart.md
│ │ └── webpack.config.js
│ └── mcp-server-web-research
│ ├── .env.example
│ ├── .gitignore
│ ├── .vscode
│ │ ├── launch.json
│ │ └── settings.json
│ ├── Makefile
│ ├── mcp_server_web_research
│ │ ├── __init__.py
│ │ ├── azure_openai.py
│ │ ├── config.py
│ │ ├── logging.py
│ │ ├── sampling.py
│ │ ├── server.py
│ │ ├── start.py
│ │ ├── utils.py
│ │ └── web_research.py
│ ├── pyproject.toml
│ ├── README.md
│ ├── test
│ │ └── test_web_research.py
│ └── uv.lock
├── README.md
├── RESPONSIBLE_AI_FAQ.md
├── ruff.toml
├── SECURITY.md
├── semantic-workbench.code-workspace
├── SUPPORT.md
├── tools
│ ├── build_ai_context_files.py
│ ├── collect_files.py
│ ├── docker
│ │ ├── azure_website_sshd.conf
│ │ ├── docker-entrypoint.sh
│ │ ├── Dockerfile.assistant
│ │ └── Dockerfile.mcp-server
│ ├── makefiles
│ │ ├── docker-assistant.mk
│ │ ├── docker-mcp-server.mk
│ │ ├── docker.mk
│ │ ├── python.mk
│ │ ├── recursive.mk
│ │ └── shell.mk
│ ├── reset-service-data.ps1
│ ├── reset-service-data.sh
│ ├── run-app.ps1
│ ├── run-app.sh
│ ├── run-canonical-agent.ps1
│ ├── run-canonical-agent.sh
│ ├── run-dotnet-examples-with-aspire.sh
│ ├── run-python-example1.sh
│ ├── run-python-example2.ps1
│ ├── run-python-example2.sh
│ ├── run-service.ps1
│ ├── run-service.sh
│ ├── run-workbench-chatbot.ps1
│ └── run-workbench-chatbot.sh
├── workbench-app
│ ├── .dockerignore
│ ├── .env.example
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── .vscode
│ │ ├── launch.json
│ │ └── settings.json
│ ├── docker-entrypoint.sh
│ ├── Dockerfile
│ ├── docs
│ │ ├── APP_DEV_GUIDE.md
│ │ ├── MESSAGE_METADATA.md
│ │ ├── MESSAGE_TYPES.md
│ │ ├── README.md
│ │ └── STATE_INSPECTORS.md
│ ├── index.html
│ ├── Makefile
│ ├── nginx.conf
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── prettier.config.cjs
│ ├── public
│ │ └── assets
│ │ ├── background-1-upscaled.jpg
│ │ ├── background-1-upscaled.png
│ │ ├── background-1.jpg
│ │ ├── background-1.png
│ │ ├── background-2.jpg
│ │ ├── background-2.png
│ │ ├── experimental-feature.jpg
│ │ ├── favicon.svg
│ │ ├── workflow-designer-1.jpg
│ │ ├── workflow-designer-outlets.jpg
│ │ ├── workflow-designer-states.jpg
│ │ └── workflow-designer-transitions.jpg
│ ├── README.md
│ ├── run.sh
│ ├── src
│ │ ├── components
│ │ │ ├── App
│ │ │ │ ├── AppFooter.tsx
│ │ │ │ ├── AppHeader.tsx
│ │ │ │ ├── AppMenu.tsx
│ │ │ │ ├── AppView.tsx
│ │ │ │ ├── CodeLabel.tsx
│ │ │ │ ├── CommandButton.tsx
│ │ │ │ ├── ConfirmLeave.tsx
│ │ │ │ ├── ContentExport.tsx
│ │ │ │ ├── ContentImport.tsx
│ │ │ │ ├── CopyButton.tsx
│ │ │ │ ├── DialogControl.tsx
│ │ │ │ ├── DynamicIframe.tsx
│ │ │ │ ├── ErrorListFromAppState.tsx
│ │ │ │ ├── ErrorMessageBar.tsx
│ │ │ │ ├── ExperimentalNotice.tsx
│ │ │ │ ├── FormWidgets
│ │ │ │ │ ├── BaseModelEditorWidget.tsx
│ │ │ │ │ ├── CustomizedArrayFieldTemplate.tsx
│ │ │ │ │ ├── CustomizedFieldTemplate.tsx
│ │ │ │ │ ├── CustomizedObjectFieldTemplate.tsx
│ │ │ │ │ └── InspectableWidget.tsx
│ │ │ │ ├── LabelWithDescription.tsx
│ │ │ │ ├── Loading.tsx
│ │ │ │ ├── MenuItemControl.tsx
│ │ │ │ ├── MiniControl.tsx
│ │ │ │ ├── MyAssistantServiceRegistrations.tsx
│ │ │ │ ├── MyItemsManager.tsx
│ │ │ │ ├── OverflowMenu.tsx
│ │ │ │ ├── PresenceMotionList.tsx
│ │ │ │ ├── ProfileSettings.tsx
│ │ │ │ └── TooltipWrapper.tsx
│ │ │ ├── Assistants
│ │ │ │ ├── ApplyConfigButton.tsx
│ │ │ │ ├── AssistantAdd.tsx
│ │ │ │ ├── AssistantConfigExportButton.tsx
│ │ │ │ ├── AssistantConfigImportButton.tsx
│ │ │ │ ├── AssistantConfiguration.tsx
│ │ │ │ ├── AssistantConfigure.tsx
│ │ │ │ ├── AssistantCreate.tsx
│ │ │ │ ├── AssistantDelete.tsx
│ │ │ │ ├── AssistantDuplicate.tsx
│ │ │ │ ├── AssistantExport.tsx
│ │ │ │ ├── AssistantImport.tsx
│ │ │ │ ├── AssistantRemove.tsx
│ │ │ │ ├── AssistantRename.tsx
│ │ │ │ ├── AssistantServiceInfo.tsx
│ │ │ │ ├── AssistantServiceMetadata.tsx
│ │ │ │ └── MyAssistants.tsx
│ │ │ ├── AssistantServiceRegistrations
│ │ │ │ ├── AssistantServiceRegistrationApiKey.tsx
│ │ │ │ ├── AssistantServiceRegistrationApiKeyReset.tsx
│ │ │ │ ├── AssistantServiceRegistrationCreate.tsx
│ │ │ │ └── AssistantServiceRegistrationRemove.tsx
│ │ │ ├── Conversations
│ │ │ │ ├── Canvas
│ │ │ │ │ ├── AssistantCanvas.tsx
│ │ │ │ │ ├── AssistantCanvasList.tsx
│ │ │ │ │ ├── AssistantInspector.tsx
│ │ │ │ │ ├── AssistantInspectorList.tsx
│ │ │ │ │ └── ConversationCanvas.tsx
│ │ │ │ ├── ChatInputPlugins
│ │ │ │ │ ├── ClearEditorPlugin.tsx
│ │ │ │ │ ├── LexicalMenu.ts
│ │ │ │ │ ├── ParticipantMentionsPlugin.tsx
│ │ │ │ │ ├── TypeaheadMenuPlugin.css
│ │ │ │ │ └── TypeaheadMenuPlugin.tsx
│ │ │ │ ├── ContentRenderers
│ │ │ │ │ ├── CodeContentRenderer.tsx
│ │ │ │ │ ├── ContentListRenderer.tsx
│ │ │ │ │ ├── ContentRenderer.tsx
│ │ │ │ │ ├── DiffRenderer.tsx
│ │ │ │ │ ├── HtmlContentRenderer.tsx
│ │ │ │ │ ├── JsonSchemaContentRenderer.tsx
│ │ │ │ │ ├── MarkdownContentRenderer.tsx
│ │ │ │ │ ├── MarkdownEditorRenderer.tsx
│ │ │ │ │ ├── MermaidContentRenderer.tsx
│ │ │ │ │ ├── MusicABCContentRenderer.css
│ │ │ │ │ └── MusicABCContentRenderer.tsx
│ │ │ │ ├── ContextWindow.tsx
│ │ │ │ ├── ConversationCreate.tsx
│ │ │ │ ├── ConversationDuplicate.tsx
│ │ │ │ ├── ConversationExport.tsx
│ │ │ │ ├── ConversationFileIcon.tsx
│ │ │ │ ├── ConversationRemove.tsx
│ │ │ │ ├── ConversationRename.tsx
│ │ │ │ ├── ConversationShare.tsx
│ │ │ │ ├── ConversationShareCreate.tsx
│ │ │ │ ├── ConversationShareList.tsx
│ │ │ │ ├── ConversationShareView.tsx
│ │ │ │ ├── ConversationsImport.tsx
│ │ │ │ ├── ConversationTranscript.tsx
│ │ │ │ ├── DebugInspector.tsx
│ │ │ │ ├── FileItem.tsx
│ │ │ │ ├── FileList.tsx
│ │ │ │ ├── InputAttachmentList.tsx
│ │ │ │ ├── InputOptionsControl.tsx
│ │ │ │ ├── InteractHistory.tsx
│ │ │ │ ├── InteractInput.tsx
│ │ │ │ ├── Message
│ │ │ │ │ ├── AttachmentSection.tsx
│ │ │ │ │ ├── ContentRenderer.tsx
│ │ │ │ │ ├── ContentSafetyNotice.tsx
│ │ │ │ │ ├── InteractMessage.tsx
│ │ │ │ │ ├── MessageActions.tsx
│ │ │ │ │ ├── MessageBase.tsx
│ │ │ │ │ ├── MessageBody.tsx
│ │ │ │ │ ├── MessageContent.tsx
│ │ │ │ │ ├── MessageFooter.tsx
│ │ │ │ │ ├── MessageHeader.tsx
│ │ │ │ │ ├── NotificationAccordion.tsx
│ │ │ │ │ └── ToolResultMessage.tsx
│ │ │ │ ├── MessageDelete.tsx
│ │ │ │ ├── MessageLink.tsx
│ │ │ │ ├── MyConversations.tsx
│ │ │ │ ├── MyShares.tsx
│ │ │ │ ├── ParticipantAvatar.tsx
│ │ │ │ ├── ParticipantAvatarGroup.tsx
│ │ │ │ ├── ParticipantItem.tsx
│ │ │ │ ├── ParticipantList.tsx
│ │ │ │ ├── ParticipantStatus.tsx
│ │ │ │ ├── RewindConversation.tsx
│ │ │ │ ├── ShareRemove.tsx
│ │ │ │ ├── SpeechButton.tsx
│ │ │ │ └── ToolCalls.tsx
│ │ │ └── FrontDoor
│ │ │ ├── Chat
│ │ │ │ ├── AssistantDrawer.tsx
│ │ │ │ ├── CanvasDrawer.tsx
│ │ │ │ ├── Chat.tsx
│ │ │ │ ├── ChatCanvas.tsx
│ │ │ │ ├── ChatControls.tsx
│ │ │ │ └── ConversationDrawer.tsx
│ │ │ ├── Controls
│ │ │ │ ├── AssistantCard.tsx
│ │ │ │ ├── AssistantSelector.tsx
│ │ │ │ ├── AssistantServiceSelector.tsx
│ │ │ │ ├── ConversationItem.tsx
│ │ │ │ ├── ConversationList.tsx
│ │ │ │ ├── ConversationListOptions.tsx
│ │ │ │ ├── NewConversationButton.tsx
│ │ │ │ ├── NewConversationForm.tsx
│ │ │ │ └── SiteMenuButton.tsx
│ │ │ ├── GlobalContent.tsx
│ │ │ └── MainContent.tsx
│ │ ├── Constants.ts
│ │ ├── global.d.ts
│ │ ├── index.css
│ │ ├── libs
│ │ │ ├── AppStorage.ts
│ │ │ ├── AuthHelper.ts
│ │ │ ├── EventSubscriptionManager.ts
│ │ │ ├── Theme.ts
│ │ │ ├── useAssistantCapabilities.ts
│ │ │ ├── useChatCanvasController.ts
│ │ │ ├── useConversationEvents.ts
│ │ │ ├── useConversationUtility.ts
│ │ │ ├── useCreateConversation.ts
│ │ │ ├── useDebugComponentLifecycle.ts
│ │ │ ├── useDragAndDrop.ts
│ │ │ ├── useEnvironment.ts
│ │ │ ├── useExportUtility.ts
│ │ │ ├── useHistoryUtility.ts
│ │ │ ├── useKeySequence.ts
│ │ │ ├── useMediaQuery.ts
│ │ │ ├── useMicrosoftGraph.ts
│ │ │ ├── useNotify.tsx
│ │ │ ├── useParticipantUtility.tsx
│ │ │ ├── useSiteUtility.ts
│ │ │ ├── useWorkbenchEventSource.ts
│ │ │ ├── useWorkbenchService.ts
│ │ │ └── Utility.ts
│ │ ├── main.tsx
│ │ ├── models
│ │ │ ├── Assistant.ts
│ │ │ ├── AssistantCapability.ts
│ │ │ ├── AssistantServiceInfo.ts
│ │ │ ├── AssistantServiceRegistration.ts
│ │ │ ├── Config.ts
│ │ │ ├── Conversation.ts
│ │ │ ├── ConversationFile.ts
│ │ │ ├── ConversationMessage.ts
│ │ │ ├── ConversationMessageDebug.ts
│ │ │ ├── ConversationParticipant.ts
│ │ │ ├── ConversationShare.ts
│ │ │ ├── ConversationShareRedemption.ts
│ │ │ ├── ConversationState.ts
│ │ │ ├── ConversationStateDescription.ts
│ │ │ ├── ServiceEnvironment.ts
│ │ │ └── User.ts
│ │ ├── redux
│ │ │ ├── app
│ │ │ │ ├── hooks.ts
│ │ │ │ ├── rtkQueryErrorLogger.ts
│ │ │ │ └── store.ts
│ │ │ └── features
│ │ │ ├── app
│ │ │ │ ├── appSlice.ts
│ │ │ │ └── AppState.ts
│ │ │ ├── chatCanvas
│ │ │ │ ├── chatCanvasSlice.ts
│ │ │ │ └── ChatCanvasState.ts
│ │ │ ├── localUser
│ │ │ │ ├── localUserSlice.ts
│ │ │ │ └── LocalUserState.ts
│ │ │ └── settings
│ │ │ ├── settingsSlice.ts
│ │ │ └── SettingsState.ts
│ │ ├── Root.tsx
│ │ ├── routes
│ │ │ ├── AcceptTerms.tsx
│ │ │ ├── AssistantEditor.tsx
│ │ │ ├── AssistantServiceRegistrationEditor.tsx
│ │ │ ├── Dashboard.tsx
│ │ │ ├── ErrorPage.tsx
│ │ │ ├── FrontDoor.tsx
│ │ │ ├── Login.tsx
│ │ │ ├── Settings.tsx
│ │ │ ├── ShareRedeem.tsx
│ │ │ └── Shares.tsx
│ │ ├── services
│ │ │ └── workbench
│ │ │ ├── assistant.ts
│ │ │ ├── assistantService.ts
│ │ │ ├── conversation.ts
│ │ │ ├── file.ts
│ │ │ ├── index.ts
│ │ │ ├── participant.ts
│ │ │ ├── share.ts
│ │ │ ├── state.ts
│ │ │ └── workbench.ts
│ │ └── vite-env.d.ts
│ ├── tools
│ │ └── filtered-ts-prune.cjs
│ ├── tsconfig.json
│ └── vite.config.ts
└── workbench-service
├── .env.example
├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ └── settings.json
├── alembic.ini
├── devdb
│ ├── docker-compose.yaml
│ └── postgresql-init.sh
├── Dockerfile
├── Makefile
├── migrations
│ ├── env.py
│ ├── README
│ ├── script.py.mako
│ └── versions
│ ├── 2024_09_19_000000_69dcda481c14_init.py
│ ├── 2024_09_19_190029_dffb1d7e219a_file_version_filename.py
│ ├── 2024_09_20_204130_b29524775484_share.py
│ ├── 2024_10_30_231536_039bec8edc33_index_message_type.py
│ ├── 2024_11_04_204029_5149c7fb5a32_conversationmessagedebug.py
│ ├── 2024_11_05_015124_245baf258e11_double_check_debugs.py
│ ├── 2024_11_25_191056_a106de176394_drop_workflow.py
│ ├── 2025_03_19_140136_aaaf792d4d72_set_user_title_set.py
│ ├── 2025_03_21_153250_3763629295ad_add_assistant_template_id.py
│ ├── 2025_05_19_163613_b2f86e981885_delete_context_transfer_assistants.py
│ └── 2025_06_18_174328_503c739152f3_delete_knowlege_transfer_assistants.py
├── pyproject.toml
├── README.md
├── semantic_workbench_service
│ ├── __init__.py
│ ├── api.py
│ ├── assistant_api_key.py
│ ├── auth.py
│ ├── azure_speech.py
│ ├── config.py
│ ├── controller
│ │ ├── __init__.py
│ │ ├── assistant_service_client_pool.py
│ │ ├── assistant_service_registration.py
│ │ ├── assistant.py
│ │ ├── conversation_share.py
│ │ ├── conversation.py
│ │ ├── convert.py
│ │ ├── exceptions.py
│ │ ├── export_import.py
│ │ ├── file.py
│ │ ├── participant.py
│ │ └── user.py
│ ├── db.py
│ ├── event.py
│ ├── files.py
│ ├── logging_config.py
│ ├── middleware.py
│ ├── query.py
│ ├── service_user_principals.py
│ ├── service.py
│ └── start.py
├── tests
│ ├── __init__.py
│ ├── conftest.py
│ ├── docker-compose.yaml
│ ├── test_assistant_api_key.py
│ ├── test_files.py
│ ├── test_integration.py
│ ├── test_middleware.py
│ ├── test_migrations.py
│ ├── test_workbench_service.py
│ └── types.py
└── uv.lock
```
# Files
--------------------------------------------------------------------------------
/assistants/prospector-assistant/assistant/chat.py:
--------------------------------------------------------------------------------
```python
1 | # Copyright (c) Microsoft. All rights reserved.
2 |
3 | # Prospector Assistant
4 | #
5 | # This assistant helps you mine ideas from artifacts.
6 | #
7 |
8 | import asyncio
9 | import logging
10 | import re
11 | import traceback
12 | from contextlib import asynccontextmanager
13 | from typing import Any, Awaitable, Callable
14 |
15 | import deepmerge
16 | import openai_client
17 | from assistant_extensions.attachments import AttachmentsExtension
18 | from content_safety.evaluators import CombinedContentSafetyEvaluator
19 | from openai.types.chat import ChatCompletionMessageParam
20 | from llm_client.model import CompletionMessageImageContent
21 | from pydantic import BaseModel, ConfigDict
22 | from semantic_workbench_api_model.workbench_model import (
23 | AssistantStateEvent,
24 | ConversationEvent,
25 | ConversationMessage,
26 | ConversationParticipant,
27 | MessageType,
28 | NewConversationMessage,
29 | )
30 | from semantic_workbench_assistant.assistant_app import (
31 | AssistantApp,
32 | BaseModelAssistantConfig,
33 | ContentSafety,
34 | ContentSafetyEvaluator,
35 | ConversationContext,
36 | )
37 |
38 | from . import legacy
39 | from .agents.artifact_agent import Artifact, ArtifactAgent, ArtifactConversationInspectorStateProvider
40 | from .agents.document_agent import DocumentAgent
41 | from .artifact_creation_extension.extension import ArtifactCreationExtension
42 | from .config import AssistantConfigModel
43 | from .form_fill_extension import FormFillExtension, LLMConfig
44 |
45 | logger = logging.getLogger(__name__)
46 |
47 | #
48 | # region Setup
49 | #
50 |
51 | # the service id to be registered in the workbench to identify the assistant
52 | service_id = "prospector-assistant.made-exploration"
53 | # the name of the assistant service, as it will appear in the workbench UI
54 | service_name = "Prospector Assistant"
55 | # a description of the assistant service, as it will appear in the workbench UI
56 | service_description = "An assistant that helps you mine ideas from artifacts."
57 |
58 | #
59 | # create the configuration provider, using the extended configuration model
60 | #
61 | assistant_config = BaseModelAssistantConfig(AssistantConfigModel)
62 |
63 |
64 | # define the content safety evaluator factory
65 | async def content_evaluator_factory(context: ConversationContext) -> ContentSafetyEvaluator:
66 | config = await assistant_config.get(context.assistant)
67 | return CombinedContentSafetyEvaluator(config.content_safety_config)
68 |
69 |
70 | content_safety = ContentSafety(content_evaluator_factory)
71 |
72 | # create the AssistantApp instance
73 | assistant = AssistantApp(
74 | assistant_service_id=service_id,
75 | assistant_service_name=service_name,
76 | assistant_service_description=service_description,
77 | config_provider=assistant_config.provider,
78 | content_interceptor=content_safety,
79 | inspector_state_providers={
80 | "artifacts": ArtifactConversationInspectorStateProvider(assistant_config),
81 | },
82 | )
83 |
84 | attachments_extension = AttachmentsExtension(assistant)
85 | form_fill_extension = FormFillExtension(assistant)
86 | artifact_creation_extension = ArtifactCreationExtension(assistant, assistant_config)
87 |
88 | #
89 | # create the FastAPI app instance
90 | #
91 | app = assistant.fastapi_app()
92 |
93 |
94 | # endregion
95 |
96 |
97 | #
98 | # region Event Handlers
99 | #
100 | # The AssistantApp class provides a set of decorators for adding event handlers to respond to conversation
101 | # events. In VS Code, typing "@assistant." (or the name of your AssistantApp instance) will show available
102 | # events and methods.
103 | #
104 | # See the semantic-workbench-assistant AssistantApp class for more information on available events and methods.
105 | # Examples:
106 | # - @assistant.events.conversation.on_created (event triggered when the assistant is added to a conversation)
107 | # - @assistant.events.conversation.participant.on_created (event triggered when a participant is added)
108 | # - @assistant.events.conversation.message.on_created (event triggered when a new message of any type is created)
109 | # - @assistant.events.conversation.message.chat.on_created (event triggered when a new chat message is created)
110 | #
111 |
112 |
113 | @assistant.events.conversation.message.on_created
114 | async def on_message_created(
115 | context: ConversationContext, event: ConversationEvent, message: ConversationMessage
116 | ) -> None:
117 | await legacy.provide_guidance_if_necessary(context)
118 |
119 |
120 | @assistant.events.conversation.message.chat.on_created
121 | async def on_chat_message_created(
122 | context: ConversationContext, event: ConversationEvent, message: ConversationMessage
123 | ) -> None:
124 | """
125 | Handle the event triggered when a new chat message is created in the conversation.
126 |
127 | **Note**
128 | - This event handler is specific to chat messages.
129 | - To handle other message types, you can add additional event handlers for those message types.
130 | - @assistant.events.conversation.message.log.on_created
131 | - @assistant.events.conversation.message.command.on_created
132 | - ...additional message types
133 | - To handle all message types, you can use the root event handler for all message types:
134 | - @assistant.events.conversation.message.on_created
135 | """
136 |
137 | config = await assistant_config.get(context.assistant)
138 | if config.guided_workflow == "Long Document Creation":
139 | return
140 |
141 | # update the participant status to indicate the assistant is responding
142 | async with send_error_message_on_exception(context), context.set_status("responding..."):
143 | #
144 | # NOTE: we're experimenting with agents, if they are enabled, use them to respond to the conversation
145 | #
146 | metadata: dict[str, Any] = {"debug": {"content_safety": event.data.get(content_safety.metadata_key, {})}}
147 |
148 | match config.guided_workflow:
149 | case "Form Completion":
150 | await form_fill_execute(context, message)
151 | case "Document Creation":
152 | await create_document_execute(config, context, message, metadata)
153 | case _:
154 | logger.error("Guided workflow unknown or not supported.")
155 |
156 |
157 | background_tasks: set[asyncio.Task] = set()
158 |
159 |
160 | @assistant.events.conversation.on_created
161 | async def on_conversation_created(context: ConversationContext) -> None:
162 | """
163 | Handle the event triggered when the assistant is added to a conversation.
164 | """
165 | assistant_sent_messages = await context.get_messages(participant_ids=[context.assistant.id], limit=1)
166 | welcome_sent_before = len(assistant_sent_messages.messages) > 0
167 | if welcome_sent_before:
168 | return
169 |
170 | #
171 | # NOTE: we're experimenting with agents, if they are enabled, use them to respond to the conversation
172 | #
173 | config = await assistant_config.get(context.assistant)
174 | metadata: dict[str, Any] = {"debug": {}}
175 |
176 | task: asyncio.Task | None = None
177 | match config.guided_workflow:
178 | case "Form Completion":
179 | task = asyncio.create_task(welcome_message_form_fill(context))
180 | case "Document Creation":
181 | task = asyncio.create_task(
182 | welcome_message_create_document(config, context, message=None, metadata=metadata)
183 | )
184 | case "Long Document Creation":
185 | pass
186 | case _:
187 | logger.error("Guided workflow unknown or not supported.")
188 | return
189 |
190 | if task:
191 | background_tasks.add(task)
192 | task.add_done_callback(background_tasks.remove)
193 |
194 |
195 | async def welcome_message_form_fill(context: ConversationContext) -> None:
196 | async with send_error_message_on_exception(context), context.set_status("responding..."):
197 | await form_fill_execute(context, None)
198 |
199 |
200 | async def welcome_message_create_document(
201 | config: AssistantConfigModel,
202 | context: ConversationContext,
203 | message: ConversationMessage | None,
204 | metadata: dict[str, Any],
205 | ) -> None:
206 | async with send_error_message_on_exception(context), context.set_status("responding..."):
207 | await create_document_execute(config, context, message, metadata)
208 |
209 |
210 | @asynccontextmanager
211 | async def send_error_message_on_exception(context: ConversationContext):
212 | try:
213 | yield
214 | except Exception as e:
215 | await context.send_messages(
216 | NewConversationMessage(
217 | content=f"An error occurred: {e}",
218 | message_type=MessageType.notice,
219 | metadata={"debug": {"stack_trace": traceback.format_exc()}},
220 | )
221 | )
222 |
223 |
224 | # endregion
225 |
226 | #
227 | # region Form Fill Extension Helpers
228 | #
229 |
230 |
231 | async def form_fill_execute(context: ConversationContext, message: ConversationMessage | None) -> None:
232 | """
233 | Execute the form fill agent to respond to the conversation message.
234 | """
235 | config = await assistant_config.get(context.assistant)
236 | participants = await context.get_participants(include_inactive=True)
237 | await form_fill_extension.execute(
238 | llm_config=LLMConfig(
239 | openai_client_factory=lambda: openai_client.create_client(config.service_config),
240 | openai_model=config.request_config.openai_model,
241 | max_response_tokens=config.request_config.response_tokens,
242 | ),
243 | config=config.agents_config.form_fill_agent,
244 | context=context,
245 | latest_user_message=_format_message(message, participants.participants) if message else None,
246 | latest_attachment_filenames=message.filenames if message else [],
247 | get_attachment_content=form_fill_extension_get_attachment(context, config),
248 | )
249 |
250 |
251 | def form_fill_extension_get_attachment(
252 | context: ConversationContext, config: AssistantConfigModel
253 | ) -> Callable[[str], Awaitable[str]]:
254 | """Helper function for the form_fill_extension to get the content of an attachment by filename."""
255 |
256 | async def get(filename: str) -> str:
257 | messages = await attachments_extension.get_completion_messages_for_attachments(
258 | context,
259 | config.agents_config.attachment_agent,
260 | include_filenames=[filename],
261 | )
262 | if not messages:
263 | return ""
264 |
265 | # filter down to the message with the attachment
266 | user_message = next(
267 | (message for message in messages if "<ATTACHMENT>" in str(message)),
268 | None,
269 | )
270 | if not user_message:
271 | return ""
272 |
273 | content = user_message.content
274 | match content:
275 | case str():
276 | return content
277 |
278 | case list():
279 | for part in content:
280 | match part:
281 | case CompletionMessageImageContent():
282 | return part.data
283 |
284 | return ""
285 |
286 | return get
287 |
288 |
289 | # endregion
290 |
291 |
292 | #
293 | # region Document Extension Helpers
294 | #
295 |
296 |
297 | async def create_document_execute(
298 | config: AssistantConfigModel,
299 | context: ConversationContext,
300 | message: ConversationMessage | None,
301 | metadata: dict[str, Any] = {},
302 | ) -> None:
303 | """
304 | Respond to a conversation message using the document agent.
305 | """
306 | # create the document agent instance
307 | document_agent = DocumentAgent(attachments_extension)
308 | await document_agent.create_document(config, context, message, metadata)
309 |
310 |
311 | # demonstrates how to respond to a conversation message using the OpenAI API.
312 | async def respond_to_conversation(
313 | context: ConversationContext,
314 | config: AssistantConfigModel,
315 | message: ConversationMessage,
316 | metadata: dict[str, Any] = {},
317 | ) -> None:
318 | """
319 | Respond to a conversation message.
320 |
321 | This method uses the OpenAI API to generate a response to the message.
322 |
323 | It includes any attachments as individual system messages before the chat history, along with references
324 | to the attachments in the point in the conversation where they were mentioned. This allows the model to
325 | consider the full contents of the attachments separate from the conversation, but with the context of
326 | where they were mentioned and any relevant surrounding context such as how to interpret the attachment
327 | or why it was shared or what to do with it.
328 | """
329 |
330 | # define the metadata key for any metadata created within this method
331 | method_metadata_key = "respond_to_conversation"
332 |
333 | # get the list of conversation participants
334 | participants_response = await context.get_participants(include_inactive=True)
335 |
336 | # establish a token to be used by the AI model to indicate no response
337 | silence_token = "{{SILENCE}}"
338 |
339 | system_message_content = f'{config.instruction_prompt}\n\nYour name is "{context.assistant.name}".'
340 | if len(participants_response.participants) > 2:
341 | system_message_content += (
342 | "\n\n"
343 | f"There are {len(participants_response.participants)} participants in the conversation,"
344 | " including you as the assistant and the following users:"
345 | + ",".join([
346 | f' "{participant.name}"'
347 | for participant in participants_response.participants
348 | if participant.id != context.assistant.id
349 | ])
350 | + "\n\nYou do not need to respond to every message. Do not respond if the last thing said was a closing"
351 | " statement such as 'bye' or 'goodbye', or just a general acknowledgement like 'ok' or 'thanks'. Do not"
352 | f' respond as another user in the conversation, only as "{context.assistant.name}".'
353 | " Sometimes the other users need to talk amongst themselves and that is ok. If the conversation seems to"
354 | f' be directed at you or the general audience, go ahead and respond.\n\nSay "{silence_token}" to skip'
355 | " your turn."
356 | )
357 |
358 | # add the artifact agent instruction prompt to the system message content
359 | if config.agents_config.artifact_agent.enabled:
360 | system_message_content += f"\n\n{config.agents_config.artifact_agent.instruction_prompt}"
361 |
362 | # add the guardrails prompt to the system message content
363 | system_message_content += f"\n\n{config.guardrails_prompt}"
364 |
365 | completion_messages: list[ChatCompletionMessageParam] = [
366 | {
367 | "role": "system",
368 | "content": system_message_content,
369 | }
370 | ]
371 |
372 | # generate the attachment messages from the attachment agent
373 | attachment_messages = await attachments_extension.get_completion_messages_for_attachments(
374 | context, config=config.agents_config.attachment_agent
375 | )
376 |
377 | # add the attachment messages to the completion messages
378 | completion_messages.extend(openai_client.convert_from_completion_messages(attachment_messages))
379 |
380 | # get messages before the current message
381 | messages_response = await context.get_messages(before=message.id)
382 | messages = messages_response.messages + [message]
383 |
384 | # calculate the token count for the messages so far
385 | token_count = openai_client.num_tokens_from_messages(
386 | model=config.request_config.openai_model, messages=completion_messages
387 | )
388 |
389 | # calculate the total available tokens for the response generation
390 | available_tokens = config.request_config.max_tokens - config.request_config.response_tokens
391 |
392 | # build the completion messages from the conversation history
393 | history_messages: list[ChatCompletionMessageParam] = []
394 |
395 | # add the messages in reverse order to get the most recent messages first
396 | for message in reversed(messages):
397 | messages_to_add: list[ChatCompletionMessageParam] = []
398 |
399 | # add the message to the completion messages, treating any message from a source other than the assistant
400 | # as a user message
401 | if message.sender.participant_id == context.assistant.id:
402 | messages_to_add.append({
403 | "role": "assistant",
404 | "content": _format_message(message, participants_response.participants),
405 | })
406 | else:
407 | # we are working with the messages in reverse order, so include any attachments before the message
408 | if message.filenames and len(message.filenames) > 0:
409 | # add a system message to indicate the attachments
410 | messages_to_add.append({
411 | "role": "system",
412 | "content": f"Attachment(s): {', '.join(message.filenames)}",
413 | })
414 | # add the user message to the completion messages
415 | messages_to_add.append({
416 | "role": "user",
417 | "content": _format_message(message, participants_response.participants),
418 | })
419 |
420 | # calculate the token count for the message and check if it exceeds the available tokens
421 | messages_to_add_token_count = openai_client.num_tokens_from_messages(
422 | model=config.request_config.openai_model, messages=messages_to_add
423 | )
424 | if (token_count + messages_to_add_token_count) > available_tokens:
425 | # stop processing messages if the token count exceeds the available tokens
426 | break
427 |
428 | token_count += messages_to_add_token_count
429 | history_messages.extend(messages_to_add)
430 |
431 | # reverse the history messages to get them back in the correct order
432 | history_messages.reverse()
433 |
434 | # add the history messages to the completion messages
435 | completion_messages.extend(history_messages)
436 |
437 | # initialize variables for the response content and total tokens used
438 | content: str | None = None
439 | completion_total_tokens: int | None = None
440 |
441 | # set default response message type
442 | message_type = MessageType.chat
443 |
444 | # TODO: DRY up this code by moving the OpenAI API call to a shared method and calling it from both branches
445 | # use structured response support to create or update artifacts, if artifacts are enabled
446 | if config.agents_config.artifact_agent.enabled:
447 | # define the structured response format for the AI model
448 | class StructuredResponseFormat(BaseModel):
449 | model_config = ConfigDict(
450 | extra="forbid",
451 | json_schema_extra={
452 | "description": (
453 | "The response format for the assistant. Use the assistant_response field for the"
454 | " response content and the artifacts_to_create_or_update field for any artifacts"
455 | " to create or update."
456 | ),
457 | "required": ["assistant_response", "artifacts_to_create_or_update"],
458 | },
459 | )
460 |
461 | assistant_response: str
462 | artifacts_to_create_or_update: list[Artifact]
463 |
464 | # generate a response from the AI model
465 | completion_total_tokens: int | None = None
466 | async with openai_client.create_client(config.service_config) as client:
467 | try:
468 | # call the OpenAI API to generate a completion
469 | completion = await client.beta.chat.completions.parse(
470 | messages=completion_messages,
471 | model=config.request_config.openai_model,
472 | max_tokens=config.request_config.response_tokens,
473 | response_format=StructuredResponseFormat,
474 | )
475 | content = completion.choices[0].message.content
476 |
477 | # get the prospector response from the completion
478 | structured_response = completion.choices[0].message.parsed
479 | # get the assistant response from the prospector response
480 | content = structured_response.assistant_response if structured_response else content
481 | # get the artifacts to create or update from the prospector response
482 | if structured_response and structured_response.artifacts_to_create_or_update:
483 | for artifact in structured_response.artifacts_to_create_or_update:
484 | ArtifactAgent.create_or_update_artifact(
485 | context,
486 | artifact,
487 | )
488 | # send an event to notify the artifact state was updated
489 | await context.send_conversation_state_event(
490 | AssistantStateEvent(
491 | state_id="artifacts",
492 | event="updated",
493 | state=None,
494 | )
495 | )
496 |
497 | # send a focus event to notify the assistant to focus on the artifacts
498 | await context.send_conversation_state_event(
499 | AssistantStateEvent(
500 | state_id="artifacts",
501 | event="focus",
502 | state=None,
503 | )
504 | )
505 |
506 | # get the total tokens used for the completion
507 | completion_total_tokens = completion.usage.total_tokens if completion.usage else None
508 |
509 | # add the completion to the metadata for debugging
510 | deepmerge.always_merger.merge(
511 | metadata,
512 | {
513 | "debug": {
514 | method_metadata_key: {
515 | "request": {
516 | "model": config.request_config.openai_model,
517 | "messages": completion_messages,
518 | "max_tokens": config.request_config.response_tokens,
519 | "response_format": StructuredResponseFormat.model_json_schema(),
520 | },
521 | "response": completion.model_dump() if completion else "[no response from openai]",
522 | },
523 | }
524 | },
525 | )
526 | except Exception as e:
527 | logger.exception(f"exception occurred calling openai chat completion: {e}")
528 | content = (
529 | "An error occurred while calling the OpenAI API. Is it configured correctly?"
530 | " View the debug inspector for more information."
531 | )
532 | message_type = MessageType.notice
533 | deepmerge.always_merger.merge(
534 | metadata,
535 | {
536 | "debug": {
537 | method_metadata_key: {
538 | "request": {
539 | "model": config.request_config.openai_model,
540 | "messages": completion_messages,
541 | },
542 | "error": str(e),
543 | },
544 | }
545 | },
546 | )
547 |
548 | # fallback to prior approach to generate a response from the AI model when artifacts are not enabled
549 | if not config.agents_config.artifact_agent.enabled:
550 | # generate a response from the AI model
551 | completion_total_tokens: int | None = None
552 | async with openai_client.create_client(config.service_config) as client:
553 | try:
554 | # call the OpenAI API to generate a completion
555 | completion = await client.chat.completions.create(
556 | messages=completion_messages,
557 | model=config.request_config.openai_model,
558 | max_tokens=config.request_config.response_tokens,
559 | )
560 | content = completion.choices[0].message.content
561 |
562 | # get the total tokens used for the completion
563 | completion_total_tokens = completion.usage.total_tokens if completion.usage else None
564 |
565 | # add the completion to the metadata for debugging
566 | deepmerge.always_merger.merge(
567 | metadata,
568 | {
569 | "debug": {
570 | method_metadata_key: {
571 | "request": {
572 | "model": config.request_config.openai_model,
573 | "messages": completion_messages,
574 | "max_tokens": config.request_config.response_tokens,
575 | },
576 | "response": completion.model_dump() if completion else "[no response from openai]",
577 | },
578 | }
579 | },
580 | )
581 |
582 | except Exception as e:
583 | logger.exception(f"exception occurred calling openai chat completion: {e}")
584 | content = (
585 | "An error occurred while calling the OpenAI API. Is it configured correctly?"
586 | " View the debug inspector for more information."
587 | )
588 | message_type = MessageType.notice
589 | deepmerge.always_merger.merge(
590 | metadata,
591 | {
592 | "debug": {
593 | method_metadata_key: {
594 | "request": {
595 | "model": config.request_config.openai_model,
596 | "messages": completion_messages,
597 | },
598 | "error": str(e),
599 | },
600 | }
601 | },
602 | )
603 |
604 | if content:
605 | # strip out the username from the response
606 | if content.startswith("["):
607 | content = re.sub(r"\[.*\]:\s", "", content)
608 |
609 | # model sometimes puts extra spaces in the response, so remove them
610 | # when checking for the silence token
611 | if content.replace(" ", "") == silence_token:
612 | # if debug output is enabled, notify the conversation that the assistant chose to remain silent
613 | if config.enable_debug_output:
614 | # add debug metadata to indicate the assistant chose to remain silent
615 | deepmerge.always_merger.merge(
616 | metadata,
617 | {
618 | "debug": {
619 | method_metadata_key: {
620 | "silence_token": True,
621 | },
622 | },
623 | "attribution": "debug output",
624 | "generated_content": False,
625 | },
626 | )
627 | # send a notice message to the conversation
628 | await context.send_messages(
629 | NewConversationMessage(
630 | message_type=MessageType.notice,
631 | content="[assistant chose to remain silent]",
632 | metadata=metadata,
633 | )
634 | )
635 | return
636 |
637 | # override message type if content starts with /
638 | if content.startswith("/"):
639 | message_type = MessageType.command_response
640 |
641 | # send the response to the conversation
642 | await context.send_messages(
643 | NewConversationMessage(
644 | content=content or "[no response from openai]",
645 | message_type=message_type,
646 | metadata=metadata,
647 | )
648 | )
649 |
650 | # check the token usage and send a warning if it is high
651 | if completion_total_tokens is not None and config.high_token_usage_warning.enabled:
652 | # calculate the token count for the warning threshold
653 | token_count_for_warning = config.request_config.max_tokens * (config.high_token_usage_warning.threshold / 100)
654 |
655 | # check if the completion total tokens exceed the warning threshold
656 | if completion_total_tokens > token_count_for_warning:
657 | content = f"{config.high_token_usage_warning.message}\n\nTotal tokens used: {completion_total_tokens}"
658 |
659 | # send a notice message to the conversation that the token usage is high
660 | await context.send_messages(
661 | NewConversationMessage(
662 | content=content,
663 | message_type=MessageType.notice,
664 | metadata={
665 | "debug": {
666 | "high_token_usage_warning": {
667 | "completion_total_tokens": completion_total_tokens,
668 | "threshold": config.high_token_usage_warning.threshold,
669 | "token_count_for_warning": token_count_for_warning,
670 | }
671 | },
672 | "attribution": "system",
673 | },
674 | )
675 | )
676 |
677 |
678 | # endregion
679 |
680 |
681 | #
682 | # region Helpers
683 | #
684 |
685 |
686 | def _format_message(message: ConversationMessage, participants: list[ConversationParticipant]) -> str:
687 | """
688 | Format a conversation message for display.
689 | """
690 | conversation_participant = next(
691 | (participant for participant in participants if participant.id == message.sender.participant_id),
692 | None,
693 | )
694 | participant_name = conversation_participant.name if conversation_participant else "unknown"
695 | message_datetime = message.timestamp.strftime("%Y-%m-%d %H:%M:%S")
696 | return f"[{participant_name} - {message_datetime}]: {message.content}"
697 |
698 |
699 | # endregion
700 |
```
--------------------------------------------------------------------------------
/libraries/python/skills/skill-library/skill_library/cli/run_routine.py:
--------------------------------------------------------------------------------
```python
1 | #!/usr/bin/env python3
2 | """
3 | run-routine - Command-line utility to execute routines from the skill_library
4 |
5 | Usage:
6 | run-routine ROUTINE_NAME [OPTIONS]
7 | run-routine --list
8 | run-routine --usage
9 | cat input.txt | run-routine ROUTINE_NAME [OPTIONS]
10 |
11 | Arguments:
12 | ROUTINE_NAME Name of the routine to run (e.g., "research2.research")
13 |
14 | Options:
15 | --home PATH Path to skills home directory (default: .skills in current or home dir)
16 | --engine ID Engine ID to use (default: "cli")
17 | --param KEY=VALUE Parameter to pass to the routine (can be used multiple times)
18 | --timeout SECONDS Maximum seconds to allow the routine to run (default: 600)
19 | --non-interactive Run in non-interactive mode, fail if user input is needed
20 | --list List all available routines
21 | --usage Show detailed usage information for all routines
22 | --quiet Suppress log output to stderr in interactive mode
23 |
24 | Environment Variables:
25 | SKILLS_HOME_DIR Override skills home directory path
26 | SKILLS_ENGINE_ID Override engine ID
27 |
28 | Examples:
29 | # Run a research routine with a specific topic
30 | run-routine research2.research --param plan_name="AI-trends" --param topic="Latest AI trends"
31 |
32 | # Pipe content as input
33 | echo "What is quantum computing?" | run-routine research2.research
34 |
35 | # List all available routines
36 | run-routine --list
37 |
38 | # See detailed usage for all routines
39 | run-routine --usage
40 |
41 | # Run with a 5-minute timeout
42 | run-routine research2.research --param plan_name="climate" --param topic="Climate change" --timeout 300
43 |
44 | # Run in non-interactive mode (will fail if user input is needed)
45 | run-routine research2.research --param plan_name="ai-ethics" --param topic="AI ethics" --non-interactive
46 | """
47 |
48 | import asyncio
49 | import datetime
50 | import importlib
51 | import json
52 | import os
53 | import sys
54 | import time
55 | import traceback
56 | import uuid
57 | from argparse import ArgumentParser, RawDescriptionHelpFormatter
58 | from pathlib import Path
59 | from typing import Any, Dict, Optional, Tuple
60 |
61 | from skill_library.skills.meta.meta_skill import MetaSkill, MetaSkillConfig
62 |
63 | # Import skill library dependencies
64 | try:
65 | from assistant_drive import Drive, DriveConfig
66 | from events import events as skill_events
67 | from semantic_workbench_api_model.workbench_model import (
68 | ParticipantRole,
69 | )
70 | from skill_library import Engine
71 | from skill_library.cli.azure_openai import create_azure_openai_client
72 | from skill_library.cli.conversation_history import ConversationHistory
73 | from skill_library.cli.settings import Settings
74 | from skill_library.cli.skill_logger import SkillLogger
75 |
76 | # Import skill implementations
77 | from skill_library.skills.common import CommonSkill, CommonSkillConfig
78 | from skill_library.skills.posix import PosixSkill, PosixSkillConfig
79 | from skill_library.skills.research import ResearchSkill, ResearchSkillConfig
80 | from skill_library.skills.web_research import WebResearchSkill, WebResearchSkillConfig
81 | except ImportError as e:
82 | print(f"Error: Required dependency not found - {e}", file=sys.stderr)
83 | print("Please ensure skill_library and its dependencies are installed.", file=sys.stderr)
84 | sys.exit(1)
85 |
86 |
87 | class RoutineRunner:
88 | """Manages the execution of skill routines."""
89 |
90 | def __init__(
91 | self,
92 | routine_name: str,
93 | params: Dict[str, Any],
94 | input_text: Optional[str],
95 | skills_home_dir: Path,
96 | engine_id: str,
97 | history: ConversationHistory,
98 | logger: SkillLogger,
99 | ):
100 | self.routine_name = routine_name
101 | self.params = params.copy()
102 | self.input_text = input_text
103 | self.skills_home_dir = skills_home_dir
104 | self.engine_id = engine_id
105 | self.history = history
106 | self.logger = logger
107 |
108 | # Extract special parameters
109 | self.non_interactive = self.params.pop("__non_interactive", False)
110 | self.timeout_seconds = self.params.pop("__timeout", 600)
111 |
112 | # Handle input text
113 | if self.input_text:
114 | try:
115 | if self.input_text:
116 | input_json = json.loads(self.input_text)
117 | if isinstance(input_json, dict):
118 | self.params.update(input_json)
119 | elif isinstance(input_json, list):
120 | self.params["input_list"] = input_json
121 | except json.JSONDecodeError:
122 | self.params["input_text"] = self.input_text
123 |
124 | # Record input text as user message
125 | self.history.add_message(self.input_text, ParticipantRole.user)
126 |
127 | # Runtime state
128 | self.engine = None
129 | self.user_interaction_queue = asyncio.Queue()
130 | self.user_interaction_active = asyncio.Event()
131 | self.start_time = 0
132 |
133 | def initialize_engine(self) -> Optional[Engine]:
134 | """Initialize the engine with configured skills."""
135 | # Create settings from the skills home directory
136 | settings = Settings(self.skills_home_dir, self.logger)
137 |
138 | # Ensure data folder exists
139 | data_folder = settings.data_folder
140 | data_folder.mkdir(parents=True, exist_ok=True)
141 |
142 | drive = Drive(DriveConfig(root=data_folder))
143 |
144 | try:
145 | language_model = create_azure_openai_client(
146 | settings.azure_openai_endpoint, settings.azure_openai_deployment
147 | )
148 | reasoning_language_model = create_azure_openai_client(
149 | settings.azure_openai_endpoint, settings.azure_openai_reasoning_deployment
150 | )
151 | self.logger.info("Created Azure OpenAI client")
152 | except Exception as e:
153 | self.logger.error(f"Failed to create Azure OpenAI client: {e}")
154 | return None
155 |
156 | drive_root = data_folder / self.engine_id / "drive"
157 | metadata_drive_root = data_folder / self.engine_id / "metadata"
158 |
159 | drive_root.mkdir(parents=True, exist_ok=True)
160 | metadata_drive_root.mkdir(parents=True, exist_ok=True)
161 |
162 | self.logger.info(f"Initializing engine with ID: {self.engine_id}")
163 | self.logger.debug(f"Drive root: {drive_root}", {"drive_root": str(drive_root)})
164 |
165 | try:
166 | engine = Engine(
167 | engine_id=self.engine_id,
168 | message_history_provider=self.history.get_message_list,
169 | drive_root=drive_root,
170 | metadata_drive_root=metadata_drive_root,
171 | skills=[
172 | (
173 | MetaSkill,
174 | MetaSkillConfig(
175 | name="meta",
176 | drive=drive.subdrive("meta"),
177 | language_model=language_model,
178 | ),
179 | ),
180 | (
181 | CommonSkill,
182 | CommonSkillConfig(
183 | name="common",
184 | language_model=language_model,
185 | drive=drive.subdrive("common"),
186 | bing_subscription_key=settings.bing_subscription_key,
187 | bing_search_url=settings.bing_search_url,
188 | ),
189 | ),
190 | (
191 | PosixSkill,
192 | PosixSkillConfig(
193 | name="posix",
194 | sandbox_dir=data_folder / self.engine_id,
195 | mount_dir="/mnt/data",
196 | ),
197 | ),
198 | (
199 | ResearchSkill,
200 | ResearchSkillConfig(
201 | name="research",
202 | language_model=language_model,
203 | drive=drive.subdrive("research"),
204 | ),
205 | ),
206 | (
207 | WebResearchSkill,
208 | WebResearchSkillConfig(
209 | name="web_research",
210 | language_model=language_model,
211 | reasoning_language_model=reasoning_language_model,
212 | drive=drive.subdrive("web_research"),
213 | ),
214 | ),
215 | # Additional skills would be loaded based on config
216 | ],
217 | )
218 | self.logger.info("Engine initialized successfully with 4 skills")
219 | return engine
220 | except Exception as e:
221 | self.logger.error(f"Failed to initialize engine: {e}")
222 | return None
223 |
224 | async def monitor_events(self):
225 | """Monitor and process engine events."""
226 | if not self.engine:
227 | self.logger.error("Engine not initialized before monitoring events")
228 | return
229 |
230 | try:
231 | self.logger.info("Starting event monitoring")
232 |
233 | async for event in self.engine.events:
234 | event_type = type(event).__name__.replace("Event", "")
235 | event_meta: dict[str, Any] = {"event_type": event_type}
236 | if event.message:
237 | # Check if this is a message event (ask_user prompt)
238 | if isinstance(event, skill_events.MessageEvent):
239 | # Put the message in the queue for handling
240 | await self.user_interaction_queue.put(event.message)
241 | # Record the message in conversation history
242 | self.history.add_message(event.message, ParticipantRole.assistant)
243 | self.user_interaction_active.set()
244 |
245 | # Log the event for user interaction
246 | if event.metadata:
247 | event_meta.update({"event_metadata": event.metadata})
248 | self.logger.info(f"User interaction requested: {event.message}", event_meta)
249 | elif isinstance(event, skill_events.StatusUpdatedEvent):
250 | # Log status updates
251 | self.logger.info(f"Status updated: {event.message}", {"event_metadata": event.metadata})
252 | else:
253 | # Log other events at debug level
254 | if event.metadata:
255 | event_meta.update({"event_metadata": event.metadata})
256 | self.logger.debug(f"{event_type}: {event.message}", event_meta)
257 | except Exception as e:
258 | self.logger.error(f"Error monitoring events: {e}")
259 |
260 | async def handle_user_interactions(self):
261 | """Handle interactive user prompts during routine execution."""
262 | if not self.engine:
263 | self.logger.error("Engine not initialized before handling user interactions")
264 | return
265 |
266 | try:
267 | self.logger.info("Starting user interaction handler")
268 | while True:
269 | # Wait for a prompt from ask_user
270 | prompt = await self.user_interaction_queue.get()
271 |
272 | if self.non_interactive:
273 | # In non-interactive mode, fail the routine
274 | self.logger.error("Routine requires user input but --non-interactive was specified")
275 | response = "ERROR: Non-interactive mode enabled but user input was requested"
276 |
277 | # Check if engine is initialized before calling resume_routine
278 | if self.engine:
279 | await self.engine.resume_routine(response)
280 | else:
281 | self.logger.error("Cannot resume routine - engine not initialized")
282 |
283 | # Record the error response
284 | self.history.add_message(response, ParticipantRole.user)
285 | self.user_interaction_queue.task_done()
286 | self.user_interaction_active.clear()
287 | continue
288 |
289 | # Prompt the user for input via stderr - this bypasses quiet mode
290 | self.logger.prompt_user(prompt)
291 |
292 | while True:
293 | # Read from stdin in a way that doesn't block the event loop
294 | response = await asyncio.to_thread(sys.stdin.readline)
295 | response = response.strip()
296 |
297 | # Check for special commands
298 | if response.lower() == ":q":
299 | self.logger.info("User requested to quit")
300 | raise KeyboardInterrupt("User requested to quit")
301 | elif response.lower() == ":h":
302 | self._show_help()
303 | continue
304 | elif response.lower() == ":s":
305 | self._show_status()
306 | continue
307 | elif response.lower() == ":history":
308 | self.history.display_history()
309 | print("Enter your response: ", file=sys.stderr, end="", flush=True)
310 | continue
311 |
312 | # Got a valid response
313 | break
314 |
315 | # Record the user's response in conversation history
316 | self.history.add_message(response, ParticipantRole.user)
317 | self.logger.debug(f"User provided response: {response[:50]}...")
318 |
319 | # Resume the routine with the user's input
320 | if self.engine:
321 | await self.engine.resume_routine(response)
322 | else:
323 | self.logger.error("Cannot resume routine - engine not initialized")
324 |
325 | # Mark this task as done
326 | self.user_interaction_queue.task_done()
327 | self.user_interaction_active.clear()
328 | except KeyboardInterrupt:
329 | # Propagate the interrupt
330 | raise
331 | except asyncio.CancelledError:
332 | # Task was cancelled, clean exit
333 | self.logger.debug("User interaction handler cancelled")
334 | pass
335 | except Exception as e:
336 | self.logger.error(f"Error handling user interaction: {e}")
337 |
338 | def _show_help(self):
339 | """Display help for interactive commands."""
340 | self.logger.info("\nHelp:")
341 | self.logger.info(" :q - Quit the routine")
342 | self.logger.info(" :h - Show this help message")
343 | self.logger.info(" :s - Show current routine status")
344 | self.logger.info(" :history - Show conversation history")
345 | print("Enter your response: ", file=sys.stderr, end="", flush=True)
346 |
347 | def _show_status(self):
348 | """Display current routine status."""
349 | elapsed = int(time.time() - self.start_time)
350 | self.logger.info("\nCurrent routine status:")
351 | self.logger.info(f" Routine: {self.routine_name}")
352 | self.logger.info(f" Parameters: {json.dumps(self.params, indent=2)}")
353 | self.logger.info(f" Elapsed time: {elapsed} seconds")
354 | self.logger.info(f" Timeout: {self.timeout_seconds} seconds")
355 | print("Enter your response: ", file=sys.stderr, end="", flush=True)
356 |
357 | async def run(self) -> str:
358 | """Run the routine with monitoring and user interaction handling."""
359 | self.logger.info(f"Starting routine: {self.routine_name}")
360 |
361 | # Initialize engine
362 | self.engine = self.initialize_engine()
363 | if not self.engine:
364 | self.logger.error("Failed to initialize engine")
365 | return "ERROR: Failed to initialize engine"
366 |
367 | # Verify the routine exists
368 | try:
369 | available_routines = self.engine.list_routines()
370 | if self.routine_name not in available_routines:
371 | self.logger.error(f"Routine '{self.routine_name}' not found")
372 | available = "\n ".join(sorted(available_routines))
373 | return f"ERROR: Routine '{self.routine_name}' not found. Available routines:\n {available}"
374 | except AttributeError:
375 | self.logger.error("Engine does not support listing routines")
376 | return "ERROR: Engine does not support listing routines"
377 |
378 | # Start monitoring tasks
379 | monitor_task = asyncio.create_task(self.monitor_events())
380 | interaction_task = asyncio.create_task(self.handle_user_interactions())
381 |
382 | # Track start time for timeout reporting
383 | self.start_time = time.time()
384 |
385 | try:
386 | # Run the routine with timeout
387 | self.logger.info(f"Running {self.routine_name} with params: {self.params}")
388 | try:
389 | result = await asyncio.wait_for(
390 | self.engine.run_routine(self.routine_name, **self.params), timeout=self.timeout_seconds
391 | )
392 | elapsed = int(time.time() - self.start_time)
393 | self.logger.info(f"Routine completed successfully in {elapsed} seconds.")
394 |
395 | # Record the final result in conversation history
396 | self.history.add_message(str(result), ParticipantRole.assistant)
397 |
398 | return result
399 | except asyncio.TimeoutError:
400 | elapsed = int(time.time() - self.start_time)
401 | error_msg = f"Routine timed out after {elapsed} seconds (limit: {self.timeout_seconds}s)"
402 | self.logger.error(error_msg)
403 |
404 | # Record the timeout error in conversation history
405 | self.history.add_message(f"ERROR: {error_msg}", ParticipantRole.service)
406 |
407 | return f"ERROR: Routine execution timed out after {self.timeout_seconds} seconds"
408 |
409 | except KeyboardInterrupt:
410 | error_msg = "Operation cancelled by user"
411 | self.logger.warning(error_msg)
412 |
413 | # Record the cancellation in conversation history
414 | self.history.add_message(f"ERROR: {error_msg}", ParticipantRole.service)
415 |
416 | return f"ERROR: {error_msg}"
417 | except Exception as e:
418 | error_msg = f"Failed to run routine: {str(e)}"
419 | self.logger.error(error_msg, {"exception": traceback.format_exc()})
420 |
421 | # Record the error in conversation history
422 | self.history.add_message(f"ERROR: {error_msg}", ParticipantRole.service)
423 |
424 | return f"ERROR: {error_msg}"
425 | finally:
426 | # Clean up the tasks
427 | for task in [monitor_task, interaction_task]:
428 | if not task.done():
429 | task.cancel()
430 | try:
431 | await asyncio.wait_for(task, timeout=2.0)
432 | except (asyncio.CancelledError, asyncio.TimeoutError):
433 | pass
434 |
435 | # If we're in the middle of a user interaction, print a message
436 | if self.user_interaction_active.is_set():
437 | self.logger.warning("Routine execution ended while waiting for user input.")
438 |
439 | # Finalize logging
440 | self.logger.finalize()
441 |
442 |
443 | def find_skills_home_dir() -> Path:
444 | """Find the skills home directory based on environment or defaults."""
445 | # Check environment variable first
446 | if env_home_dir := os.environ.get("SKILLS_HOME_DIR"):
447 | return Path(env_home_dir)
448 |
449 | # Check current directory
450 | cwd_skills_home = Path.cwd() / ".skills"
451 | if cwd_skills_home.exists() and cwd_skills_home.is_dir():
452 | return cwd_skills_home
453 |
454 | # Fall back to home directory
455 | home_skills_dir = Path.home() / ".skills"
456 | # Create directory if it doesn't exist
457 | home_skills_dir.mkdir(parents=True, exist_ok=True)
458 |
459 | # Ensure config and data subdirectories exist
460 | (home_skills_dir / "config").mkdir(exist_ok=True)
461 | (home_skills_dir / "data").mkdir(exist_ok=True)
462 |
463 | return home_skills_dir
464 |
465 |
466 | def parse_args() -> Tuple[str, Dict[str, Any], Path, str, bool, bool]:
467 | """Parse command line arguments."""
468 | parser = ArgumentParser(description=__doc__, formatter_class=RawDescriptionHelpFormatter)
469 |
470 | # Allow routine name to be optional when using --list or --usage
471 | parser.add_argument(
472 | "routine_name", nargs="?", default="", help="Name of the routine to run (e.g., 'research2.research')"
473 | )
474 |
475 | parser.add_argument("--list", action="store_true", help="List all available routines")
476 |
477 | parser.add_argument("--usage", action="store_true", help="Show detailed usage information for all routines")
478 |
479 | parser.add_argument(
480 | "--home", help="Path to skills home directory (default: .skills in current or home dir)", type=Path
481 | )
482 |
483 | parser.add_argument(
484 | "--engine", help="Engine ID to use (default: 'cli')", default=os.environ.get("SKILLS_ENGINE_ID", "cli")
485 | )
486 |
487 | parser.add_argument(
488 | "--param", action="append", help="Parameter in KEY=VALUE format (can be used multiple times)", default=[]
489 | )
490 |
491 | parser.add_argument(
492 | "--non-interactive",
493 | action="store_true",
494 | help="Run in non-interactive mode. The routine will fail if it needs user input.",
495 | )
496 |
497 | parser.add_argument(
498 | "--timeout", type=int, default=600, help="Timeout in seconds for the routine execution (default: 600)"
499 | )
500 |
501 | parser.add_argument("--quiet", action="store_true", help="Suppress log output to stderr in interactive mode")
502 |
503 | args = parser.parse_args()
504 |
505 | # Process skills home directory
506 | skills_home_dir = args.home if args.home else find_skills_home_dir()
507 |
508 | # We have special commands like --list and --usage
509 | if args.list:
510 | return "__list__", {}, skills_home_dir, args.engine, args.non_interactive, args.quiet
511 |
512 | if args.usage:
513 | return "__usage__", {}, skills_home_dir, args.engine, args.non_interactive, args.quiet
514 |
515 | # No routine specified and no special command
516 | if not args.routine_name:
517 | parser.print_help()
518 | sys.exit(1)
519 |
520 | # Process parameters
521 | params = {}
522 | for param in args.param:
523 | try:
524 | key, value = param.split("=", 1)
525 | # Try to parse as JSON for complex types
526 | try:
527 | value = json.loads(value)
528 | except json.JSONDecodeError:
529 | pass # Keep as string if not valid JSON
530 | params[key] = value
531 | except ValueError:
532 | print(f"Warning: Ignoring invalid parameter format: {param}", file=sys.stderr)
533 |
534 | # Store non-interactive mode and timeout in params so they can be accessed by run-routine
535 | params["__non_interactive"] = args.non_interactive
536 | params["__timeout"] = args.timeout
537 |
538 | return args.routine_name, params, skills_home_dir, args.engine, args.non_interactive, args.quiet
539 |
540 |
541 | def setup_console_input():
542 | """
543 | Configure input handling for interactive use even when stdin is redirected.
544 | Returns the original stdin if modified.
545 | """
546 | # If stdin is already a TTY, no need to change anything
547 | if sys.stdin.isatty():
548 | return None
549 |
550 | # Save the original stdin for later restoration
551 | original_stdin = sys.stdin
552 |
553 | # Try to open a direct connection to the terminal
554 | try:
555 | # This works on Unix-like systems
556 | tty_stdin = open("/dev/tty", "r")
557 | sys.stdin = tty_stdin
558 | return original_stdin
559 | except Exception:
560 | # For Windows, we need a different approach
561 | if os.name == "nt":
562 | try:
563 | # Try to use msvcrt for Windows
564 | if importlib.util.find_spec("msvcrt"):
565 | # We can't actually replace stdin on Windows easily,
566 | # but we'll set a flag to use a custom input method
567 | print("Warning: Using Windows console input handling", file=sys.stderr)
568 | return original_stdin
569 | except ImportError:
570 | pass
571 |
572 |
573 | async def main() -> None:
574 | """Main entry point for the command."""
575 | # Parse arguments
576 | routine_name, params, skills_home_dir, engine_id, non_interactive, quiet = parse_args()
577 |
578 | # Generate run ID
579 | timestamp: str = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
580 | run_id = f"{timestamp}_{uuid.uuid4().hex[:8]}"
581 |
582 | # Create the logger
583 | logger = SkillLogger(skills_home_dir=skills_home_dir, run_id=run_id, interactive=not non_interactive, quiet=quiet)
584 |
585 | # Log startup information
586 | logger.info(f"Run ID: {run_id}")
587 | logger.info(f"Skills home directory: {skills_home_dir}")
588 |
589 | # Create history object for all paths
590 | history = ConversationHistory(logger)
591 |
592 | # Check if there's input on stdin
593 | input_text = None
594 | # Read from the original stdin for the initial input if stdin was redirected
595 | if "original_stdin" in globals() and globals()["original_stdin"] is not None:
596 | input_text = globals()["original_stdin"].read().strip()
597 | elif not sys.stdin.isatty():
598 | # This case should generally not happen since we handle stdin redirection
599 | # in the __main__ block, but just in case
600 | input_text = sys.stdin.read().strip()
601 |
602 | # Create a runner for initializing the engine
603 | runner = RoutineRunner(
604 | routine_name=routine_name,
605 | params=params,
606 | input_text=input_text,
607 | skills_home_dir=skills_home_dir,
608 | engine_id=engine_id,
609 | history=history,
610 | logger=logger,
611 | )
612 |
613 | engine = runner.initialize_engine()
614 | if not engine:
615 | logger.error("Failed to initialize engine")
616 | return
617 |
618 | # Special commands
619 | if routine_name == "__list__":
620 | try:
621 | logger.info("Listing available routines")
622 | available_routines = sorted(engine.list_routines())
623 | if available_routines:
624 | print("Available routines:")
625 | for routine in available_routines:
626 | print(f" {routine}")
627 | else:
628 | print("No routines available.")
629 | except AttributeError:
630 | logger.error("Engine does not support listing routines")
631 | print("ERROR: Engine does not support listing routines", file=sys.stderr)
632 | finally:
633 | logger.finalize()
634 | return
635 |
636 | if routine_name == "__usage__":
637 | try:
638 | logger.info("Retrieving routine usage information")
639 | usage_text = engine.routines_usage()
640 | if usage_text:
641 | print(usage_text)
642 | else:
643 | print("No routine usage information available.")
644 | except AttributeError:
645 | logger.error("Engine does not support retrieving routine usage")
646 | print("ERROR: Engine does not support retrieving routine usage", file=sys.stderr)
647 | finally:
648 | logger.finalize()
649 | return
650 |
651 | # Announce the start of execution
652 | logger.info(f"Executing routine: {routine_name}")
653 | if input_text:
654 | input_preview = input_text[:100] + "..." if len(input_text) > 100 else input_text
655 | logger.info(f"Input text: {input_preview}")
656 |
657 | # Run the routine
658 | result = await runner.run()
659 |
660 | # Output result to stdout
661 | print(result)
662 |
663 |
664 | def entry_point():
665 | """Entry point for the command-line interface when installed as a package."""
666 |
667 | # Need to import for dynamic import checking on Windows
668 | import importlib.util # noqa: F401
669 |
670 | # Save the program start time
671 | program_start_time = time.time()
672 |
673 | # Set up console input handling
674 | original_stdin = None
675 |
676 | try:
677 | # Configure input handling for interactive use
678 | original_stdin = setup_console_input()
679 |
680 | # Make the original stdin available to the main function
681 | if original_stdin:
682 | globals()["original_stdin"] = original_stdin
683 |
684 | # Run the main async function
685 | asyncio.run(main())
686 |
687 | # Show execution time
688 | execution_time = time.time() - program_start_time
689 | print(f"Total execution time: {execution_time:.2f} seconds", file=sys.stderr)
690 |
691 | except KeyboardInterrupt:
692 | print("\nOperation cancelled by user", file=sys.stderr)
693 | sys.exit(130)
694 | except Exception as e:
695 | print(f"Error: {e}", file=sys.stderr)
696 | # Print traceback for debugging
697 | traceback.print_exc(file=sys.stderr)
698 | sys.exit(1)
699 | finally:
700 | # Restore original stdin if we modified it
701 | if original_stdin:
702 | try:
703 | original_stdin.close()
704 | except Exception:
705 | pass
706 | sys.stdin = original_stdin
707 |
708 |
709 | if __name__ == "__main__":
710 | # Need to import for dynamic import checking on Windows
711 | import importlib.util
712 |
713 | # Save the program start time
714 | program_start_time = time.time()
715 |
716 | # Set up console input handling
717 | original_stdin = None
718 |
719 | try:
720 | # Configure input handling for interactive use
721 | original_stdin = setup_console_input()
722 |
723 | # Make the original stdin available to the main function
724 | if original_stdin:
725 | globals()["original_stdin"] = original_stdin
726 |
727 | # Run the main async function
728 | asyncio.run(main())
729 |
730 | # Show execution time
731 | execution_time = time.time() - program_start_time
732 | print(f"Total execution time: {execution_time:.2f} seconds", file=sys.stderr)
733 |
734 | except KeyboardInterrupt:
735 | print("\nOperation cancelled by user", file=sys.stderr)
736 | sys.exit(130)
737 | except Exception as e:
738 | print(f"Error: {e}", file=sys.stderr)
739 | # Print traceback for debugging
740 | traceback.print_exc(file=sys.stderr)
741 | sys.exit(1)
742 | finally:
743 | # Restore original stdin if we modified it
744 | if original_stdin:
745 | try:
746 | original_stdin.close()
747 | except Exception:
748 | pass
749 | sys.stdin = original_stdin
750 |
```