connectonion 0.6.2__py3-none-any.whl → 0.6.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- connectonion/__init__.py +46 -9
- connectonion/cli/__init__.py +11 -1
- connectonion/cli/browser_agent/__init__.py +11 -1
- connectonion/cli/browser_agent/browser.py +13 -3
- connectonion/cli/browser_agent/element_finder.py +8 -0
- connectonion/cli/browser_agent/highlight_screenshot.py +9 -1
- connectonion/cli/browser_agent/scroll.py +8 -0
- connectonion/cli/co_ai/__init__.py +6 -0
- connectonion/cli/co_ai/agent.py +87 -0
- connectonion/cli/co_ai/agents/__init__.py +5 -0
- connectonion/cli/co_ai/agents/registry.py +57 -0
- connectonion/cli/co_ai/commands/__init__.py +45 -0
- connectonion/cli/co_ai/commands/compact.py +173 -0
- connectonion/cli/co_ai/commands/cost.py +77 -0
- connectonion/cli/co_ai/commands/export.py +60 -0
- connectonion/cli/co_ai/commands/help.py +80 -0
- connectonion/cli/co_ai/commands/init.py +101 -0
- connectonion/cli/co_ai/commands/sessions.py +55 -0
- connectonion/cli/co_ai/commands/tasks.py +63 -0
- connectonion/cli/co_ai/commands/undo.py +103 -0
- connectonion/cli/co_ai/context.py +127 -0
- connectonion/cli/co_ai/main.py +52 -0
- connectonion/cli/co_ai/plugins/__init__.py +5 -0
- connectonion/cli/co_ai/plugins/system_reminder.py +154 -0
- connectonion/cli/co_ai/prompts/agents/explore.md +79 -0
- connectonion/cli/co_ai/prompts/agents/plan.md +60 -0
- connectonion/cli/co_ai/prompts/assembler.py +303 -0
- connectonion/cli/{docs/co-vibecoding-principles-docs-contexts-all-in-one.md → co_ai/prompts/connectonion/README.md} +26 -0
- connectonion/cli/co_ai/prompts/connectonion/api.md +457 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/README.md +805 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/auth.md +46 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/browser.md +235 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/copy.md +184 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/create.md +335 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/init.md +431 -0
- connectonion/cli/co_ai/prompts/connectonion/co-directory-structure.md +214 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/agent.md +1078 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/events.md +816 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/llm_do.md +256 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/max_iterations.md +362 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/models.md +641 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/plugins.md +100 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/prompts.md +122 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/tools.md +512 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/transcribe.md +156 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/trust.md +291 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/README.md +18 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/auto_debug.md +1026 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/console.md +129 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/eval-format.md +178 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/eval.md +230 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/exceptions.md +307 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/log.md +117 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/xray.md +215 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/001-choosing-input-method.md +202 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/002-choosing-llm-function-name.md +202 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/003-choosing-trust-keyword.md +141 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/004-cli-create-flow.md +117 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/005-designing-agent-network-protocol.md +503 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/006-agent-address-format.md +305 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/007-authentication-backend-design.md +240 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/008-naming-is-hard.md +228 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/009-why-connect-function.md +167 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/010-cli-ux-progressive-disclosure.md +176 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/011-global-config-identity-management.md +357 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/012-tool-execution-separation.md +259 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/013-debug-and-logging-design.md +253 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/014-hook-system-design.md +510 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/015-interactive-auto-debug-design.md +837 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/016-why-no-zero-knowledge-proofs.md +358 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/017-session-logging-and-eval-format.md +120 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/018-event-api-naming.md +274 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/019-agent-lifecycle-design.md +655 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/020-trust-system-and-network-architecture.md +503 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/021-task-storage-jsonl-design.md +496 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/022-raw-asgi-implementation.md +273 -0
- connectonion/cli/co_ai/prompts/connectonion/examples/agent_reasoning.md +62 -0
- connectonion/cli/co_ai/prompts/connectonion/examples/atomic_tools.md +24 -0
- connectonion/cli/co_ai/prompts/connectonion/examples/load_guide.md +18 -0
- connectonion/cli/co_ai/prompts/connectonion/examples.md +0 -0
- connectonion/cli/co_ai/prompts/connectonion/hook-system-options.md +364 -0
- connectonion/cli/co_ai/prompts/connectonion/index.md +162 -0
- connectonion/cli/co_ai/prompts/connectonion/integrations/README.md +12 -0
- connectonion/cli/co_ai/prompts/connectonion/integrations/auth.md +450 -0
- connectonion/cli/co_ai/prompts/connectonion/integrations/google.md +431 -0
- connectonion/cli/co_ai/prompts/connectonion/integrations/microsoft.md +370 -0
- connectonion/cli/co_ai/prompts/connectonion/network/README.md +14 -0
- connectonion/cli/co_ai/prompts/connectonion/network/connect.md +543 -0
- connectonion/cli/co_ai/prompts/connectonion/network/connection.md +538 -0
- connectonion/cli/co_ai/prompts/connectonion/network/deploy.md +123 -0
- connectonion/cli/co_ai/prompts/connectonion/network/host.md +1049 -0
- connectonion/cli/co_ai/prompts/connectonion/network/protocol/agent-relay-protocol.md +495 -0
- connectonion/cli/co_ai/prompts/connectonion/network/protocol/announce-message.md +115 -0
- connectonion/cli/co_ai/prompts/connectonion/principles.md +124 -0
- connectonion/cli/co_ai/prompts/connectonion/quickstart.md +261 -0
- connectonion/cli/co_ai/prompts/connectonion/roadmap.md +81 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/README.md +77 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/meta-agent.md +152 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/minimal.md +105 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/playwright.md +130 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/web-research.md +144 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/README.md +95 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/chat.md +181 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/divider.md +63 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/dropdown.md +83 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/footer.md +44 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/fuzzy.md +68 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/input.md +84 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/keys.md +77 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/pick.md +71 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/providers.md +89 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/status_bar.md +67 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/README.md +156 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/calendar_plugin.md +68 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/eval.md +89 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/gmail_plugin.md +68 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/image_result_formatter.md +74 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/re_act.md +86 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/shell_approval.md +69 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/README.md +81 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/diff_writer.md +138 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/get_emails.md +499 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/gmail.md +135 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/google_calendar.md +106 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/memory.md +486 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/microsoft_calendar.md +106 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/outlook.md +120 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/send_email.md +403 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/shell.md +95 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/slash_command.md +96 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/terminal.md +97 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/todo_list.md +252 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/web_fetch.md +130 -0
- connectonion/cli/co_ai/prompts/connectonion/vibe-coding-guide.md +97 -0
- connectonion/cli/co_ai/prompts/connectonion/windows-support.md +258 -0
- connectonion/cli/co_ai/prompts/main.md +247 -0
- connectonion/cli/co_ai/prompts/summarization.md +55 -0
- connectonion/cli/co_ai/prompts/system-reminders/agent.md +23 -0
- connectonion/cli/co_ai/prompts/system-reminders/plan_mode.md +13 -0
- connectonion/cli/co_ai/prompts/system-reminders/security.md +14 -0
- connectonion/cli/co_ai/prompts/system-reminders/simplicity.md +14 -0
- connectonion/cli/co_ai/prompts/tools/ask_user.md +61 -0
- connectonion/cli/co_ai/prompts/tools/background.md +57 -0
- connectonion/cli/co_ai/prompts/tools/edit.md +90 -0
- connectonion/cli/co_ai/prompts/tools/glob.md +52 -0
- connectonion/cli/co_ai/prompts/tools/grep.md +55 -0
- connectonion/cli/co_ai/prompts/tools/plan_mode.md +80 -0
- connectonion/cli/co_ai/prompts/tools/read.md +40 -0
- connectonion/cli/co_ai/prompts/tools/shell.md +67 -0
- connectonion/cli/co_ai/prompts/tools/task.md +51 -0
- connectonion/cli/co_ai/prompts/tools/todo.md +139 -0
- connectonion/cli/co_ai/prompts/tools/write.md +47 -0
- connectonion/cli/co_ai/prompts/workflow.md +89 -0
- connectonion/cli/co_ai/sessions.py +110 -0
- connectonion/cli/co_ai/skills/__init__.py +37 -0
- connectonion/cli/co_ai/skills/builtin/commit/SKILL.md +63 -0
- connectonion/cli/co_ai/skills/builtin/review-pr/SKILL.md +76 -0
- connectonion/cli/co_ai/skills/loader.py +166 -0
- connectonion/cli/co_ai/skills/tool.py +46 -0
- connectonion/cli/co_ai/tools/__init__.py +92 -0
- connectonion/cli/co_ai/tools/ask_user.py +35 -0
- connectonion/cli/co_ai/tools/background.py +201 -0
- connectonion/cli/co_ai/tools/diff_writer.py +291 -0
- connectonion/cli/co_ai/tools/edit.py +89 -0
- connectonion/cli/co_ai/tools/glob.py +84 -0
- connectonion/cli/co_ai/tools/grep.py +158 -0
- connectonion/cli/co_ai/tools/load_guide.py +23 -0
- connectonion/cli/co_ai/tools/multi_edit.py +116 -0
- connectonion/cli/co_ai/tools/plan_mode.py +169 -0
- connectonion/cli/co_ai/tools/read.py +61 -0
- connectonion/cli/co_ai/tools/task.py +59 -0
- connectonion/cli/co_ai/tools/todo_list.py +159 -0
- connectonion/cli/co_ai/tools/write.py +126 -0
- connectonion/cli/commands/__init__.py +11 -1
- connectonion/cli/commands/ai_commands.py +34 -0
- connectonion/cli/commands/copy_commands.py +55 -6
- connectonion/cli/commands/create.py +20 -17
- connectonion/cli/commands/init.py +19 -22
- connectonion/cli/commands/project_cmd_lib.py +15 -0
- connectonion/cli/main.py +11 -0
- connectonion/console.py +15 -1
- connectonion/core/__init__.py +10 -1
- connectonion/core/agent.py +37 -16
- connectonion/core/exceptions.py +74 -0
- connectonion/core/llm.py +54 -6
- connectonion/core/tool_executor.py +32 -31
- connectonion/core/tool_factory.py +47 -10
- connectonion/debug/__init__.py +10 -1
- connectonion/debug/debug_explainer/__init__.py +10 -1
- connectonion/debug/execution_analyzer/__init__.py +10 -1
- connectonion/debug/execution_analyzer/execution_analysis.py +5 -2
- connectonion/debug/runtime_inspector/__init__.py +10 -1
- connectonion/docs/.package-ignore +6 -0
- connectonion/docs/README.md +2036 -0
- connectonion/docs/api.md +457 -0
- connectonion/docs/archive/001-ai-agent-is-just-prompt-plus-function.md +249 -0
- connectonion/docs/archive/README.md +53 -0
- connectonion/docs/archive/archive/consolidation-plan.md +72 -0
- connectonion/docs/archive/archive/core-principles-extracted.md +239 -0
- connectonion/docs/archive/archive/master-principles.md +222 -0
- connectonion/docs/archive/archive/principles.md +293 -0
- connectonion/docs/archive/archive/simplicity-principles.md +221 -0
- connectonion/docs/archive/attack-defense-insights.md +410 -0
- connectonion/docs/archive/business-model.md +305 -0
- connectonion/docs/archive/core-principles-unified.md +190 -0
- connectonion/docs/archive/discussion-journey.md +178 -0
- connectonion/docs/archive/economic-analysis.md +323 -0
- connectonion/docs/archive/features/01-share-and-find.md +256 -0
- connectonion/docs/archive/features/02-agent-authentication.md +93 -0
- connectonion/docs/archive/features/03-test-before-trust.md +71 -0
- connectonion/docs/archive/features/06-reliability-and-offline.md +197 -0
- connectonion/docs/archive/features/README.md +46 -0
- connectonion/docs/archive/features-roadmap.md +247 -0
- connectonion/docs/archive/mcp-comparison-insights.md +215 -0
- connectonion/docs/archive/migration-strategy.md +571 -0
- connectonion/docs/archive/mini-whitepaper.md +293 -0
- connectonion/docs/archive/network-protocol.md +394 -0
- connectonion/docs/archive/semantic-revolution.md +367 -0
- connectonion/docs/archive/technical-architecture.md +453 -0
- connectonion/docs/archive/the-semantic-insight.md +207 -0
- connectonion/docs/archive/threat-model.md +164 -0
- connectonion/docs/cli/README.md +805 -0
- connectonion/docs/cli/auth.md +46 -0
- connectonion/docs/cli/browser.md +235 -0
- connectonion/docs/cli/copy.md +232 -0
- connectonion/docs/cli/create.md +335 -0
- connectonion/docs/cli/init.md +431 -0
- connectonion/docs/co-directory-structure.md +214 -0
- connectonion/docs/concepts/agent.md +1078 -0
- connectonion/docs/concepts/events.md +699 -0
- connectonion/docs/concepts/llm_do.md +256 -0
- connectonion/docs/concepts/max_iterations.md +362 -0
- connectonion/docs/concepts/models.md +641 -0
- connectonion/docs/concepts/plugins.md +101 -0
- connectonion/docs/concepts/prompts.md +122 -0
- connectonion/docs/concepts/session.md +428 -0
- connectonion/docs/concepts/tools.md +512 -0
- connectonion/docs/concepts/transcribe.md +156 -0
- connectonion/docs/concepts/trust.md +291 -0
- connectonion/docs/connectonion.md +1256 -0
- connectonion/docs/debug/README.md +18 -0
- connectonion/docs/debug/auto_debug.md +1026 -0
- connectonion/docs/debug/console.md +129 -0
- connectonion/docs/debug/eval-format.md +178 -0
- connectonion/docs/debug/eval.md +230 -0
- connectonion/docs/debug/exceptions.md +307 -0
- connectonion/docs/debug/log.md +117 -0
- connectonion/docs/debug/xray.md +215 -0
- connectonion/docs/design-decisions/001-choosing-input-method.md +202 -0
- connectonion/docs/design-decisions/002-choosing-llm-function-name.md +202 -0
- connectonion/docs/design-decisions/003-choosing-trust-keyword.md +141 -0
- connectonion/docs/design-decisions/004-cli-create-flow.md +117 -0
- connectonion/docs/design-decisions/005-designing-agent-network-protocol.md +503 -0
- connectonion/docs/design-decisions/006-agent-address-format.md +305 -0
- connectonion/docs/design-decisions/007-authentication-backend-design.md +240 -0
- connectonion/docs/design-decisions/008-naming-is-hard.md +228 -0
- connectonion/docs/design-decisions/009-why-connect-function.md +167 -0
- connectonion/docs/design-decisions/010-cli-ux-progressive-disclosure.md +176 -0
- connectonion/docs/design-decisions/011-global-config-identity-management.md +357 -0
- connectonion/docs/design-decisions/012-tool-execution-separation.md +259 -0
- connectonion/docs/design-decisions/013-debug-and-logging-design.md +253 -0
- connectonion/docs/design-decisions/014-hook-system-design.md +510 -0
- connectonion/docs/design-decisions/015-interactive-auto-debug-design.md +837 -0
- connectonion/docs/design-decisions/016-why-no-zero-knowledge-proofs.md +358 -0
- connectonion/docs/design-decisions/017-session-logging-and-eval-format.md +120 -0
- connectonion/docs/design-decisions/018-event-api-naming.md +274 -0
- connectonion/docs/design-decisions/019-agent-lifecycle-design.md +655 -0
- connectonion/docs/design-decisions/020-trust-system-and-network-architecture.md +503 -0
- connectonion/docs/design-decisions/021-task-storage-jsonl-design.md +496 -0
- connectonion/docs/design-decisions/022-raw-asgi-implementation.md +273 -0
- connectonion/docs/examples.md +0 -0
- connectonion/docs/hook-system-options.md +364 -0
- connectonion/docs/integrations/README.md +12 -0
- connectonion/docs/integrations/auth.md +450 -0
- connectonion/docs/integrations/google.md +431 -0
- connectonion/docs/integrations/microsoft.md +370 -0
- connectonion/docs/network/README.md +14 -0
- connectonion/docs/network/connect.md +629 -0
- connectonion/docs/network/deploy.md +124 -0
- connectonion/docs/network/host.md +1087 -0
- connectonion/docs/network/io.md +538 -0
- connectonion/docs/network/protocol/agent-relay-protocol.md +495 -0
- connectonion/docs/network/protocol/announce-message.md +115 -0
- connectonion/docs/principles.md +124 -0
- connectonion/docs/quickstart.md +261 -0
- connectonion/docs/roadmap.md +81 -0
- connectonion/docs/templates/README.md +77 -0
- connectonion/docs/templates/meta-agent.md +152 -0
- connectonion/docs/templates/minimal.md +105 -0
- connectonion/docs/templates/playwright.md +130 -0
- connectonion/docs/templates/web-research.md +144 -0
- connectonion/docs/tui/README.md +95 -0
- connectonion/docs/tui/chat.md +181 -0
- connectonion/docs/tui/divider.md +63 -0
- connectonion/docs/tui/dropdown.md +83 -0
- connectonion/docs/tui/footer.md +44 -0
- connectonion/docs/tui/fuzzy.md +68 -0
- connectonion/docs/tui/input.md +84 -0
- connectonion/docs/tui/keys.md +77 -0
- connectonion/docs/tui/pick.md +71 -0
- connectonion/docs/tui/providers.md +89 -0
- connectonion/docs/tui/status_bar.md +67 -0
- connectonion/docs/useful_plugins/README.md +160 -0
- connectonion/docs/useful_plugins/calendar_plugin.md +68 -0
- connectonion/docs/useful_plugins/eval.md +89 -0
- connectonion/docs/useful_plugins/gmail_plugin.md +68 -0
- connectonion/docs/useful_plugins/image_result_formatter.md +74 -0
- connectonion/docs/useful_plugins/re_act.md +86 -0
- connectonion/docs/useful_plugins/shell_approval.md +69 -0
- connectonion/docs/useful_plugins/system_reminder.md +210 -0
- connectonion/docs/useful_plugins/tool_approval.md +139 -0
- connectonion/docs/useful_prompts/README.md +127 -0
- connectonion/docs/useful_prompts/coding_agent.md +214 -0
- connectonion/docs/useful_tools/README.md +81 -0
- connectonion/docs/useful_tools/ask_user.md +103 -0
- connectonion/docs/useful_tools/diff_writer.md +158 -0
- connectonion/docs/useful_tools/get_emails.md +519 -0
- connectonion/docs/useful_tools/gmail.md +155 -0
- connectonion/docs/useful_tools/google_calendar.md +126 -0
- connectonion/docs/useful_tools/memory.md +506 -0
- connectonion/docs/useful_tools/microsoft_calendar.md +126 -0
- connectonion/docs/useful_tools/outlook.md +140 -0
- connectonion/docs/useful_tools/send_email.md +423 -0
- connectonion/docs/useful_tools/shell.md +115 -0
- connectonion/docs/useful_tools/slash_command.md +116 -0
- connectonion/docs/useful_tools/terminal.md +115 -0
- connectonion/docs/useful_tools/todo_list.md +272 -0
- connectonion/docs/useful_tools/web_fetch.md +150 -0
- connectonion/docs/vibe-coding-guide.md +97 -0
- connectonion/docs/windows-support.md +258 -0
- connectonion/logger.py +3 -3
- connectonion/network/__init__.py +19 -6
- connectonion/network/asgi/__init__.py +81 -0
- connectonion/network/asgi/http.py +205 -0
- connectonion/network/asgi/websocket.py +217 -0
- connectonion/network/connect.py +232 -185
- connectonion/network/host/__init__.py +59 -0
- connectonion/network/host/auth.py +191 -0
- connectonion/network/host/routes.py +135 -0
- connectonion/network/host/server.py +289 -0
- connectonion/network/host/session.py +78 -0
- connectonion/network/io/__init__.py +21 -0
- connectonion/network/{connection.py → io/base.py} +17 -42
- connectonion/network/io/websocket.py +55 -0
- connectonion/network/relay.py +37 -16
- connectonion/network/trust/__init__.py +30 -0
- connectonion/network/trust/factory.py +138 -0
- connectonion/network/{trust_agents.py → trust/prompts.py} +3 -3
- connectonion/network/{trust_functions.py → trust/tools.py} +2 -2
- connectonion/prompt_files/__init__.py +11 -1
- connectonion/prompt_files/react_acknowledge.md +26 -0
- connectonion/prompts.py +10 -1
- connectonion/tui/chat.py +10 -1
- connectonion/tui/divider.py +10 -1
- connectonion/tui/dropdown.py +10 -1
- connectonion/tui/footer.py +8 -0
- connectonion/tui/fuzzy.py +11 -1
- connectonion/tui/input.py +118 -70
- connectonion/tui/keys.py +133 -6
- connectonion/tui/providers.py +11 -1
- connectonion/tui/status_bar.py +10 -1
- connectonion/useful_events_handlers/__init__.py +8 -0
- connectonion/useful_events_handlers/reflect.py +19 -4
- connectonion/useful_plugins/__init__.py +3 -1
- connectonion/useful_plugins/eval.py +2 -2
- connectonion/useful_plugins/gmail_plugin.py +3 -3
- connectonion/useful_plugins/image_result_formatter.py +3 -3
- connectonion/useful_plugins/re_act.py +114 -28
- connectonion/useful_plugins/shell_approval.py +2 -2
- connectonion/useful_plugins/system_reminder.py +103 -0
- connectonion/useful_plugins/tool_approval.py +233 -0
- connectonion/useful_plugins/ui_stream.py +18 -133
- connectonion/useful_prompts/README.md +61 -0
- connectonion/useful_prompts/__init__.py +45 -0
- connectonion/useful_prompts/coding_agent/README.md +106 -0
- connectonion/useful_prompts/coding_agent/assembler.py +123 -0
- connectonion/useful_prompts/coding_agent/prompts/main.md +227 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/ask_user.md +61 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/background.md +57 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/edit.md +90 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/glob.md +52 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/grep.md +55 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/plan_mode.md +80 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/read.md +40 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/shell.md +67 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/task.md +51 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/todo.md +139 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/write.md +48 -0
- connectonion/useful_prompts/system-reminders/security-warning.md +14 -0
- connectonion/useful_prompts/system-reminders/test-reminder.md +11 -0
- connectonion/useful_tools/__init__.py +31 -4
- connectonion/useful_tools/ask_user.py +35 -0
- connectonion/useful_tools/bash.py +69 -0
- connectonion/useful_tools/diff_writer.py +186 -94
- connectonion/useful_tools/edit.py +102 -0
- connectonion/useful_tools/glob_files.py +97 -0
- connectonion/useful_tools/grep_files.py +171 -0
- connectonion/useful_tools/multi_edit.py +116 -0
- connectonion/useful_tools/read_file.py +73 -0
- connectonion/useful_tools/shell.py +50 -45
- connectonion/useful_tools/write_file.py +129 -0
- {connectonion-0.6.2.dist-info → connectonion-0.6.4.dist-info}/METADATA +10 -3
- connectonion-0.6.4.dist-info/RECORD +472 -0
- connectonion/network/asgi.py +0 -407
- connectonion/network/host.py +0 -616
- connectonion/network/trust.py +0 -166
- connectonion-0.6.2.dist-info/RECORD +0 -129
- /connectonion/cli/{docs → co_ai/prompts/connectonion}/connectonion.md +0 -0
- {connectonion-0.6.2.dist-info → connectonion-0.6.4.dist-info}/WHEEL +0 -0
- {connectonion-0.6.2.dist-info → connectonion-0.6.4.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: HTTP request/response handling for ASGI server with Pydantic serialization
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [pydantic.BaseModel, json, hmac, os, pathlib] | imported by [network/asgi/__init__.py] | tested by [tests/network/test_asgi_http.py]
|
|
5
|
+
Data flow: handle_http() receives ASGI scope/receive/send → reads body via read_body() → parses JSON → routes to handlers (POST /input, GET /sessions, etc.) → sends response via send_json()/send_text()/send_html() | pydantic_json_encoder() serializes Pydantic models (e.g., TokenUsage) in responses | OPTIONS requests return CORS headers
|
|
6
|
+
State/Effects: reads request body from ASGI receive channel | writes response to ASGI send channel | no persistent state (stateless handler)
|
|
7
|
+
Integration: exposes read_body(receive) → bytes, send_json(send, data, status), send_text(send, text, status), send_html(send, html, status), handle_http(scope, receive, send, route_handlers, storage, trust, start_time, blacklist, whitelist) | pydantic_json_encoder(obj) as default for json.dumps() | CORS_HEADERS for cross-origin requests
|
|
8
|
+
Performance: streams body reading via ASGI receive | JSON encoding with custom Pydantic serializer | CORS headers allow browser clients
|
|
9
|
+
Errors: returns 401 for auth failures | 403 for trust violations | 404 for missing sessions/routes | 500 for handler exceptions | OPTIONS requests return 200 with CORS
|
|
10
|
+
HTTP request handling for ASGI.
|
|
11
|
+
|
|
12
|
+
This module provides HTTP request/response utilities for the ASGI server,
|
|
13
|
+
including JSON serialization with Pydantic model support.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import hmac
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from pydantic import BaseModel
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def pydantic_json_encoder(obj):
|
|
25
|
+
"""Custom JSON encoder that serializes Pydantic models to dictionaries.
|
|
26
|
+
|
|
27
|
+
Used as the `default` parameter for json.dumps() to handle Pydantic models
|
|
28
|
+
that would otherwise raise TypeError during JSON serialization.
|
|
29
|
+
|
|
30
|
+
This is needed because agent responses may contain Pydantic models like
|
|
31
|
+
TokenUsage, and we need to serialize them to JSON for HTTP/WebSocket responses.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
obj: The object to serialize. If it's a Pydantic BaseModel,
|
|
35
|
+
returns obj.model_dump(). Otherwise raises TypeError.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
dict: The serialized Pydantic model as a dictionary.
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
TypeError: If obj is not a Pydantic BaseModel.
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
>>> json.dumps({"usage": TokenUsage(input=10, output=5)},
|
|
45
|
+
... default=pydantic_json_encoder)
|
|
46
|
+
'{"usage": {"input": 10, "output": 5}}'
|
|
47
|
+
"""
|
|
48
|
+
if isinstance(obj, BaseModel):
|
|
49
|
+
return obj.model_dump()
|
|
50
|
+
# Fallback: convert unknown objects to string representation
|
|
51
|
+
# Log warning to help debug where non-serializable objects come from
|
|
52
|
+
import logging
|
|
53
|
+
logging.getLogger(__name__).warning(f"Non-JSON-serializable object: {type(obj).__name__}")
|
|
54
|
+
return f"<{type(obj).__name__}>"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# CORS headers for cross-origin requests
|
|
58
|
+
CORS_HEADERS = [
|
|
59
|
+
[b"access-control-allow-origin", b"*"],
|
|
60
|
+
[b"access-control-allow-methods", b"GET, POST, OPTIONS"],
|
|
61
|
+
[b"access-control-allow-headers", b"authorization, content-type"],
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async def read_body(receive) -> bytes:
|
|
66
|
+
"""Read complete request body from ASGI receive."""
|
|
67
|
+
body = b""
|
|
68
|
+
while True:
|
|
69
|
+
m = await receive()
|
|
70
|
+
body += m.get("body", b"")
|
|
71
|
+
if not m.get("more_body"):
|
|
72
|
+
break
|
|
73
|
+
return body
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def send_json(send, data: dict, status: int = 200):
|
|
77
|
+
"""Send JSON response via ASGI send."""
|
|
78
|
+
# Use pydantic_json_encoder to handle Pydantic models (e.g., TokenUsage) in response
|
|
79
|
+
body = json.dumps(data, default=pydantic_json_encoder).encode()
|
|
80
|
+
headers = [[b"content-type", b"application/json"]] + CORS_HEADERS
|
|
81
|
+
await send({"type": "http.response.start", "status": status, "headers": headers})
|
|
82
|
+
await send({"type": "http.response.body", "body": body})
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
async def send_html(send, html: bytes, status: int = 200):
|
|
86
|
+
"""Send HTML response via ASGI send."""
|
|
87
|
+
await send({
|
|
88
|
+
"type": "http.response.start",
|
|
89
|
+
"status": status,
|
|
90
|
+
"headers": [[b"content-type", b"text/html; charset=utf-8"]],
|
|
91
|
+
})
|
|
92
|
+
await send({"type": "http.response.body", "body": html})
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
async def send_text(send, text: str, status: int = 200):
|
|
96
|
+
"""Send plain text response via ASGI send."""
|
|
97
|
+
headers = [[b"content-type", b"text/plain; charset=utf-8"]] + CORS_HEADERS
|
|
98
|
+
await send({"type": "http.response.start", "status": status, "headers": headers})
|
|
99
|
+
await send({"type": "http.response.body", "body": text.encode()})
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
async def handle_http(
|
|
103
|
+
scope,
|
|
104
|
+
receive,
|
|
105
|
+
send,
|
|
106
|
+
*,
|
|
107
|
+
route_handlers: dict,
|
|
108
|
+
storage,
|
|
109
|
+
trust: str,
|
|
110
|
+
start_time: float,
|
|
111
|
+
blacklist: list | None = None,
|
|
112
|
+
whitelist: list | None = None,
|
|
113
|
+
):
|
|
114
|
+
"""Route HTTP requests to route handlers.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
scope: ASGI scope dict (method, path, headers, etc.)
|
|
118
|
+
receive: ASGI receive callable
|
|
119
|
+
send: ASGI send callable
|
|
120
|
+
route_handlers: Dict of route handler functions (input, session, sessions, health, info, auth)
|
|
121
|
+
storage: SessionStorage instance
|
|
122
|
+
trust: Trust level (open/careful/strict)
|
|
123
|
+
start_time: Server start time
|
|
124
|
+
blacklist: Blocked identities
|
|
125
|
+
whitelist: Allowed identities
|
|
126
|
+
"""
|
|
127
|
+
method, path = scope["method"], scope["path"]
|
|
128
|
+
|
|
129
|
+
# Handle CORS preflight requests
|
|
130
|
+
if method == "OPTIONS":
|
|
131
|
+
headers = CORS_HEADERS + [[b"content-length", b"0"]]
|
|
132
|
+
await send({"type": "http.response.start", "status": 204, "headers": headers})
|
|
133
|
+
await send({"type": "http.response.body", "body": b""})
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
# Admin endpoints require API key auth
|
|
137
|
+
if path.startswith("/admin"):
|
|
138
|
+
headers = dict(scope.get("headers", []))
|
|
139
|
+
auth = headers.get(b"authorization", b"").decode()
|
|
140
|
+
expected = os.environ.get("OPENONION_API_KEY", "")
|
|
141
|
+
if not expected or not auth.startswith("Bearer ") or not hmac.compare_digest(auth[7:], expected):
|
|
142
|
+
await send_json(send, {"error": "unauthorized"}, 401)
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
if method == "GET" and path == "/admin/logs":
|
|
146
|
+
result = route_handlers["admin_logs"]()
|
|
147
|
+
if "error" in result:
|
|
148
|
+
await send_json(send, result, 404)
|
|
149
|
+
else:
|
|
150
|
+
await send_text(send, result["content"])
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
if method == "GET" and path == "/admin/sessions":
|
|
154
|
+
await send_json(send, route_handlers["admin_sessions"]())
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
await send_json(send, {"error": "not found"}, 404)
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
if method == "POST" and path == "/input":
|
|
161
|
+
body = await read_body(receive)
|
|
162
|
+
try:
|
|
163
|
+
data = json.loads(body) if body else {}
|
|
164
|
+
except json.JSONDecodeError:
|
|
165
|
+
await send_json(send, {"error": "Invalid JSON"}, 400)
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
prompt, identity, sig_valid, err = route_handlers["auth"](
|
|
169
|
+
data, trust, blacklist=blacklist, whitelist=whitelist
|
|
170
|
+
)
|
|
171
|
+
if err:
|
|
172
|
+
status = 401 if err.startswith("unauthorized") else 403 if err.startswith("forbidden") else 400
|
|
173
|
+
await send_json(send, {"error": err}, status)
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
# Extract session for conversation continuation
|
|
177
|
+
session = data.get("session")
|
|
178
|
+
result = route_handlers["input"](storage, prompt, session)
|
|
179
|
+
await send_json(send, result)
|
|
180
|
+
|
|
181
|
+
elif method == "GET" and path.startswith("/sessions/"):
|
|
182
|
+
result = route_handlers["session"](storage, path[10:])
|
|
183
|
+
await send_json(send, result or {"error": "not found"}, 404 if not result else 200)
|
|
184
|
+
|
|
185
|
+
elif method == "GET" and path == "/sessions":
|
|
186
|
+
await send_json(send, route_handlers["sessions"](storage))
|
|
187
|
+
|
|
188
|
+
elif method == "GET" and path == "/health":
|
|
189
|
+
await send_json(send, route_handlers["health"](start_time))
|
|
190
|
+
|
|
191
|
+
elif method == "GET" and path == "/info":
|
|
192
|
+
await send_json(send, route_handlers["info"](trust))
|
|
193
|
+
|
|
194
|
+
elif method == "GET" and path == "/docs":
|
|
195
|
+
# Serve static docs page
|
|
196
|
+
try:
|
|
197
|
+
base = Path(__file__).resolve().parent.parent
|
|
198
|
+
html_path = base / "static" / "docs.html"
|
|
199
|
+
html = html_path.read_bytes()
|
|
200
|
+
except Exception:
|
|
201
|
+
html = b"<html><body><h1>ConnectOnion Docs</h1><p>Docs not found.</p></body></html>"
|
|
202
|
+
await send_html(send, html)
|
|
203
|
+
|
|
204
|
+
else:
|
|
205
|
+
await send_json(send, {"error": "not found"}, 404)
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: WebSocket bidirectional communication for ASGI server with real-time agent I/O
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [network/io/websocket.py, network/asgi/http.py pydantic_json_encoder, asyncio, json, queue, threading] | imported by [network/asgi/__init__.py] | tested by [tests/network/test_asgi_websocket.py]
|
|
5
|
+
Data flow: handle_websocket() accepts connection → receives INPUT message with prompt+session → authenticates via route_handlers["auth"] → starts agent in background thread with WebSocketIO → agent sends events via io.log()/send() → forwards to client via websocket.send → client sends ASK_USER_RESPONSE for approvals → io receives via queue → agent resumes → returns OUTPUT with result+session_id
|
|
6
|
+
State/Effects: maintains WebSocket connection during agent execution | runs agent in daemon thread (non-blocking) | uses queue.Queue for thread-safe I/O between agent and WebSocket | no persistent state (connection-scoped)
|
|
7
|
+
Integration: exposes handle_websocket(scope, receive, send, route_handlers, storage, trust, blacklist, whitelist) | uses WebSocketIO for bidirectional I/O | supports session continuation (same as HTTP) | message types: INPUT (client), OUTPUT/ERROR (server), ASK_USER_RESPONSE (client), trace events (server)
|
|
8
|
+
Performance: async WebSocket handling | agent runs in separate thread to avoid blocking | queue-based message passing | streams events in real-time (thinking, tool_result, approval_needed)
|
|
9
|
+
Errors: sends ERROR message for invalid JSON, auth failures, missing prompt | closes connection with code 4004 for wrong path | catches exceptions in agent thread and sends ERROR
|
|
10
|
+
WebSocket handling for ASGI.
|
|
11
|
+
|
|
12
|
+
ASGI Protocol Types (not our custom types - this is the ASGI spec):
|
|
13
|
+
- websocket.connect : ASGI sends when client wants to connect
|
|
14
|
+
- websocket.accept : We send to accept the connection
|
|
15
|
+
- websocket.receive : ASGI sends when client sends a message
|
|
16
|
+
- websocket.send : We send to deliver a message to client
|
|
17
|
+
- websocket.disconnect : ASGI sends when client disconnects
|
|
18
|
+
- websocket.close : We send to close the connection
|
|
19
|
+
|
|
20
|
+
Our Application Types (sent inside websocket.send text payload):
|
|
21
|
+
- INPUT : Client sends prompt
|
|
22
|
+
- OUTPUT : Server sends final result
|
|
23
|
+
- ERROR : Server sends error message
|
|
24
|
+
- ASK_USER_RESPONSE : Client responds to approval request
|
|
25
|
+
- (trace events) : thinking, tool_result, approval_needed, etc.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import asyncio
|
|
29
|
+
import json
|
|
30
|
+
import queue
|
|
31
|
+
import threading
|
|
32
|
+
|
|
33
|
+
from ..io import WebSocketIO
|
|
34
|
+
# Import pydantic_json_encoder for serializing Pydantic models (e.g., TokenUsage) in WebSocket responses
|
|
35
|
+
from .http import pydantic_json_encoder
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def handle_websocket(
|
|
39
|
+
scope,
|
|
40
|
+
receive,
|
|
41
|
+
send,
|
|
42
|
+
*,
|
|
43
|
+
route_handlers: dict,
|
|
44
|
+
storage,
|
|
45
|
+
trust: str,
|
|
46
|
+
blacklist: list | None = None,
|
|
47
|
+
whitelist: list | None = None,
|
|
48
|
+
):
|
|
49
|
+
"""Handle WebSocket connections at /ws.
|
|
50
|
+
|
|
51
|
+
Supports bidirectional communication via IO interface:
|
|
52
|
+
- Agent sends events via agent.io.log() / agent.io.send()
|
|
53
|
+
- Agent requests approval via agent.io.request_approval()
|
|
54
|
+
- Client responds to approval requests
|
|
55
|
+
|
|
56
|
+
Session support (same as HTTP):
|
|
57
|
+
- Accept session_id in INPUT message for conversation continuation
|
|
58
|
+
- Return session_id and session in OUTPUT message
|
|
59
|
+
"""
|
|
60
|
+
if scope["path"] != "/ws":
|
|
61
|
+
# ASGI: close connection with custom code
|
|
62
|
+
await send({"type": "websocket.close", "code": 4004})
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
# ASGI: accept the WebSocket connection
|
|
66
|
+
await send({"type": "websocket.accept"})
|
|
67
|
+
|
|
68
|
+
# ASGI message loop
|
|
69
|
+
while True:
|
|
70
|
+
msg = await receive() # ASGI: wait for next message
|
|
71
|
+
if msg["type"] == "websocket.disconnect": # ASGI: client disconnected
|
|
72
|
+
break
|
|
73
|
+
if msg["type"] == "websocket.receive": # ASGI: client sent a message
|
|
74
|
+
try:
|
|
75
|
+
data = json.loads(msg.get("text", "{}")) # Our app data is inside "text"
|
|
76
|
+
except json.JSONDecodeError:
|
|
77
|
+
await send({"type": "websocket.send", # ASGI: send message to client
|
|
78
|
+
"text": json.dumps({"type": "ERROR", "message": "Invalid JSON"})})
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
if data.get("type") == "INPUT": # Our app type: client wants to run agent
|
|
82
|
+
prompt, identity, sig_valid, err = route_handlers["auth"](
|
|
83
|
+
data, trust, blacklist=blacklist, whitelist=whitelist
|
|
84
|
+
)
|
|
85
|
+
if err:
|
|
86
|
+
await send({"type": "websocket.send",
|
|
87
|
+
"text": json.dumps({"type": "ERROR", "message": err})})
|
|
88
|
+
continue
|
|
89
|
+
if not prompt:
|
|
90
|
+
await send({"type": "websocket.send",
|
|
91
|
+
"text": json.dumps({"type": "ERROR", "message": "prompt required"})})
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
# Extract session for conversation continuation (same as HTTP)
|
|
95
|
+
session = data.get("session")
|
|
96
|
+
|
|
97
|
+
# Create IO for bidirectional communication
|
|
98
|
+
io = WebSocketIO()
|
|
99
|
+
agent_done = threading.Event()
|
|
100
|
+
result_holder = [None]
|
|
101
|
+
error_holder = [None]
|
|
102
|
+
|
|
103
|
+
def run_agent():
|
|
104
|
+
try:
|
|
105
|
+
result_holder[0] = route_handlers["ws_input"](storage, prompt, io, session)
|
|
106
|
+
except Exception as e:
|
|
107
|
+
error_holder[0] = str(e)
|
|
108
|
+
agent_done.set()
|
|
109
|
+
|
|
110
|
+
# Start agent in thread
|
|
111
|
+
agent_thread = threading.Thread(target=run_agent, daemon=True)
|
|
112
|
+
agent_thread.start()
|
|
113
|
+
|
|
114
|
+
# Pump messages between WebSocket and IO
|
|
115
|
+
# TODO: If client disconnects mid-request, result is still saved to SessionStorage.
|
|
116
|
+
# Client could check GET /sessions/{session_id} on reconnect to fetch pending results.
|
|
117
|
+
# For now, we just skip sending if client disconnected.
|
|
118
|
+
client_disconnected = await _pump_messages(receive, send, io, agent_done)
|
|
119
|
+
|
|
120
|
+
# Send error or final result (skip if client disconnected)
|
|
121
|
+
if client_disconnected:
|
|
122
|
+
pass # Client gone, result saved to storage, nothing to send
|
|
123
|
+
elif error_holder[0]:
|
|
124
|
+
await send({"type": "websocket.send",
|
|
125
|
+
"text": json.dumps({"type": "ERROR", "message": error_holder[0]})})
|
|
126
|
+
elif result_holder[0]:
|
|
127
|
+
result = result_holder[0]
|
|
128
|
+
await send({"type": "websocket.send",
|
|
129
|
+
"text": json.dumps({
|
|
130
|
+
"type": "OUTPUT",
|
|
131
|
+
"result": result["result"],
|
|
132
|
+
"session_id": result["session_id"],
|
|
133
|
+
"duration_ms": result["duration_ms"],
|
|
134
|
+
"session": result["session"]
|
|
135
|
+
}, default=pydantic_json_encoder)})
|
|
136
|
+
else:
|
|
137
|
+
await send({"type": "websocket.send",
|
|
138
|
+
"text": json.dumps({"type": "ERROR", "message": "Agent completed without result"})})
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
async def _pump_messages(ws_receive, ws_send, io: WebSocketIO, agent_done: threading.Event) -> bool:
|
|
142
|
+
"""Pump messages between WebSocket and IO queues.
|
|
143
|
+
|
|
144
|
+
Runs until agent completes. Handles:
|
|
145
|
+
- Outgoing: io._outgoing queue -> WebSocket
|
|
146
|
+
- Incoming: WebSocket -> io._incoming queue (for approval responses)
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
True if client disconnected before agent completed, False otherwise.
|
|
150
|
+
When True, caller should skip sending final OUTPUT (client is gone,
|
|
151
|
+
but result is already saved to SessionStorage).
|
|
152
|
+
|
|
153
|
+
Implementation note:
|
|
154
|
+
Uses asyncio.Event for signaling disconnect between nested async functions.
|
|
155
|
+
This is preferred over `nonlocal` boolean because:
|
|
156
|
+
- No risk of forgetting `nonlocal` keyword (which would create local var)
|
|
157
|
+
- Clearer intent: Event.set()/is_set() vs boolean reassignment
|
|
158
|
+
- Thread-safe if needed in future
|
|
159
|
+
"""
|
|
160
|
+
loop = asyncio.get_event_loop()
|
|
161
|
+
|
|
162
|
+
# Signal for client disconnect - shared between send/receive tasks
|
|
163
|
+
# Using Event instead of boolean avoids `nonlocal` complexity
|
|
164
|
+
disconnected = asyncio.Event()
|
|
165
|
+
|
|
166
|
+
async def send_outgoing():
|
|
167
|
+
"""Send outgoing messages from IO to WebSocket."""
|
|
168
|
+
while not agent_done.is_set() and not disconnected.is_set():
|
|
169
|
+
try:
|
|
170
|
+
event = await loop.run_in_executor(
|
|
171
|
+
None, lambda: io._outgoing.get(timeout=0.05)
|
|
172
|
+
)
|
|
173
|
+
await ws_send({"type": "websocket.send", "text": json.dumps(event, default=pydantic_json_encoder)})
|
|
174
|
+
except queue.Empty:
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
# Drain remaining messages (only if client still connected)
|
|
178
|
+
if not disconnected.is_set():
|
|
179
|
+
while True:
|
|
180
|
+
try:
|
|
181
|
+
event = io._outgoing.get_nowait()
|
|
182
|
+
await ws_send({"type": "websocket.send", "text": json.dumps(event, default=pydantic_json_encoder)})
|
|
183
|
+
except queue.Empty:
|
|
184
|
+
break
|
|
185
|
+
|
|
186
|
+
async def receive_incoming():
|
|
187
|
+
"""Receive incoming messages from WebSocket to IO."""
|
|
188
|
+
while not agent_done.is_set():
|
|
189
|
+
try:
|
|
190
|
+
msg = await asyncio.wait_for(ws_receive(), timeout=0.1)
|
|
191
|
+
if msg["type"] == "websocket.receive":
|
|
192
|
+
try:
|
|
193
|
+
data = json.loads(msg.get("text", "{}"))
|
|
194
|
+
io._incoming.put(data)
|
|
195
|
+
except json.JSONDecodeError:
|
|
196
|
+
pass
|
|
197
|
+
elif msg["type"] == "websocket.disconnect":
|
|
198
|
+
disconnected.set()
|
|
199
|
+
io.close()
|
|
200
|
+
break
|
|
201
|
+
except asyncio.TimeoutError:
|
|
202
|
+
continue
|
|
203
|
+
|
|
204
|
+
send_task = asyncio.create_task(send_outgoing())
|
|
205
|
+
recv_task = asyncio.create_task(receive_incoming())
|
|
206
|
+
|
|
207
|
+
while not agent_done.is_set():
|
|
208
|
+
await asyncio.sleep(0.05)
|
|
209
|
+
|
|
210
|
+
recv_task.cancel()
|
|
211
|
+
try:
|
|
212
|
+
await recv_task
|
|
213
|
+
except asyncio.CancelledError:
|
|
214
|
+
pass
|
|
215
|
+
await send_task
|
|
216
|
+
|
|
217
|
+
return disconnected.is_set()
|