connectonion 0.6.2__py3-none-any.whl → 0.6.3__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 +6 -0
- connectonion/cli/co_ai/plugins/reminder.py +76 -0
- connectonion/cli/co_ai/plugins/shell_approval.py +105 -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/reminders/plan_mode.md +34 -0
- connectonion/cli/co_ai/prompts/summarization.md +55 -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/reminders.py +159 -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 +172 -0
- connectonion/cli/co_ai/tools/read.py +67 -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 +100 -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_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 +2 -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/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.3.dist-info}/METADATA +10 -3
- connectonion-0.6.3.dist-info/RECORD +469 -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.3.dist-info}/WHEEL +0 -0
- {connectonion-0.6.2.dist-info → connectonion-0.6.3.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Ed25519 signature verification and trust-based authentication for hosted agents
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [network/trust/, llm_do.py, nacl.signing] | imported by [network/host/routes.py, network/host/server.py] | tested by [tests/network/test_host_auth.py]
|
|
5
|
+
Data flow: receives request dict with {payload, from, signature} → extract_and_authenticate() verifies Ed25519 signature via verify_signature() using nacl → checks timestamp expiry (5 min window) → applies trust policy (open/careful/strict/custom) → optionally evaluates via trust agent → returns (prompt, identity, sig_valid, error)
|
|
6
|
+
State/Effects: reads trust settings from agent | calls trust agent for evaluation if custom policy | no persistent state
|
|
7
|
+
Integration: exposes verify_signature(payload, signature, public_key) → bool, extract_and_authenticate(data, trust, blacklist, whitelist, agent_address) → (prompt, identity, sig_valid, error), get_agent_address(agent) → str, is_custom_trust(trust) → bool | used by host() to enforce authentication on all requests
|
|
8
|
+
Performance: signature verification uses nacl (fast Ed25519) | 5 minute timestamp window prevents replay | whitelist bypass for trusted callers
|
|
9
|
+
Errors: returns error strings: "unauthorized: ...", "forbidden: ...", "rejected: ..." | does NOT raise exceptions (caller checks error)
|
|
10
|
+
Authentication and signature verification for hosted agents.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import hashlib
|
|
14
|
+
import json
|
|
15
|
+
import time
|
|
16
|
+
|
|
17
|
+
from ..trust import create_trust_agent, TRUST_LEVELS
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Signature expiry window (5 minutes)
|
|
21
|
+
SIGNATURE_EXPIRY_SECONDS = 300
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def verify_signature(payload: dict, signature: str, public_key: str) -> bool:
|
|
25
|
+
"""Verify Ed25519 signature.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
payload: The payload that was signed
|
|
29
|
+
signature: Hex-encoded signature (with or without 0x prefix)
|
|
30
|
+
public_key: Hex-encoded public key (with or without 0x prefix)
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
True if signature is valid, False otherwise
|
|
34
|
+
"""
|
|
35
|
+
from nacl.signing import VerifyKey
|
|
36
|
+
from nacl.exceptions import BadSignatureError
|
|
37
|
+
|
|
38
|
+
# Remove 0x prefix if present
|
|
39
|
+
sig_hex = signature[2:] if signature.startswith("0x") else signature
|
|
40
|
+
key_hex = public_key[2:] if public_key.startswith("0x") else public_key
|
|
41
|
+
|
|
42
|
+
# Canonicalize payload (deterministic JSON)
|
|
43
|
+
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
verify_key = VerifyKey(bytes.fromhex(key_hex))
|
|
47
|
+
verify_key.verify(canonical.encode(), bytes.fromhex(sig_hex))
|
|
48
|
+
return True
|
|
49
|
+
except (BadSignatureError, ValueError):
|
|
50
|
+
# BadSignatureError: invalid signature
|
|
51
|
+
# ValueError: invalid hex encoding
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def extract_and_authenticate(data: dict, trust, *, blacklist=None, whitelist=None, agent_address=None):
|
|
56
|
+
"""Extract prompt and authenticate request.
|
|
57
|
+
|
|
58
|
+
ALL requests must be signed - this is a protocol requirement.
|
|
59
|
+
|
|
60
|
+
Required format (Ed25519 signed):
|
|
61
|
+
{
|
|
62
|
+
"payload": {"prompt": "...", "to": "0xAgentAddress", "timestamp": 123},
|
|
63
|
+
"from": "0xCallerPublicKey",
|
|
64
|
+
"signature": "0xEd25519Signature..."
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
Trust levels control additional policies AFTER signature verification:
|
|
68
|
+
- "open": Any valid signer allowed
|
|
69
|
+
- "careful": Warnings for unknown signers (default)
|
|
70
|
+
- "strict": Whitelist only
|
|
71
|
+
- Custom policy/Agent: LLM evaluation
|
|
72
|
+
|
|
73
|
+
Returns: (prompt, identity, sig_valid, error)
|
|
74
|
+
"""
|
|
75
|
+
# Protocol requirement: ALL requests must be signed
|
|
76
|
+
if "payload" not in data or "signature" not in data:
|
|
77
|
+
return None, None, False, "unauthorized: signed request required"
|
|
78
|
+
|
|
79
|
+
# Verify signature (protocol level - always required)
|
|
80
|
+
prompt, identity, error = _authenticate_signed(
|
|
81
|
+
data, blacklist=blacklist, whitelist=whitelist, agent_address=agent_address
|
|
82
|
+
)
|
|
83
|
+
if error:
|
|
84
|
+
return prompt, identity, False, error
|
|
85
|
+
|
|
86
|
+
# Trust level: additional policies AFTER signature verification
|
|
87
|
+
if trust == "strict" and whitelist and identity not in whitelist:
|
|
88
|
+
return None, identity, True, "forbidden: not in whitelist"
|
|
89
|
+
|
|
90
|
+
# Custom trust policy/agent evaluation
|
|
91
|
+
if is_custom_trust(trust):
|
|
92
|
+
trust_agent = create_trust_agent(trust)
|
|
93
|
+
accepted, reason = evaluate_with_trust_agent(trust_agent, prompt, identity, True)
|
|
94
|
+
if not accepted:
|
|
95
|
+
return None, identity, True, f"rejected: {reason}"
|
|
96
|
+
|
|
97
|
+
return prompt, identity, True, None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _authenticate_signed(data: dict, *, blacklist=None, whitelist=None, agent_address=None):
|
|
101
|
+
"""Authenticate signed request with Ed25519 - ALWAYS REQUIRED.
|
|
102
|
+
|
|
103
|
+
Protocol-level signature verification. All requests must be signed.
|
|
104
|
+
|
|
105
|
+
Returns: (prompt, identity, error) - error is None on success
|
|
106
|
+
"""
|
|
107
|
+
payload = data.get("payload", {})
|
|
108
|
+
identity = data.get("from")
|
|
109
|
+
signature = data.get("signature")
|
|
110
|
+
|
|
111
|
+
prompt = payload.get("prompt", "")
|
|
112
|
+
timestamp = payload.get("timestamp")
|
|
113
|
+
to_address = payload.get("to")
|
|
114
|
+
|
|
115
|
+
# Check blacklist first
|
|
116
|
+
if blacklist and identity in blacklist:
|
|
117
|
+
return None, identity, "forbidden: blacklisted"
|
|
118
|
+
|
|
119
|
+
# Check whitelist (bypass signature check - trusted caller)
|
|
120
|
+
if whitelist and identity in whitelist:
|
|
121
|
+
return prompt, identity, None
|
|
122
|
+
|
|
123
|
+
# Validate required fields
|
|
124
|
+
if not identity:
|
|
125
|
+
return None, None, "unauthorized: 'from' field required"
|
|
126
|
+
if not signature:
|
|
127
|
+
return None, identity, "unauthorized: signature required"
|
|
128
|
+
if not timestamp:
|
|
129
|
+
return None, identity, "unauthorized: timestamp required in payload"
|
|
130
|
+
|
|
131
|
+
# Check timestamp expiry (5 minute window)
|
|
132
|
+
now = time.time()
|
|
133
|
+
if abs(now - timestamp) > SIGNATURE_EXPIRY_SECONDS:
|
|
134
|
+
return None, identity, "unauthorized: signature expired"
|
|
135
|
+
|
|
136
|
+
# Optionally verify 'to' matches agent address
|
|
137
|
+
if agent_address and to_address and to_address != agent_address:
|
|
138
|
+
return None, identity, "unauthorized: wrong recipient"
|
|
139
|
+
|
|
140
|
+
# Verify signature
|
|
141
|
+
if not verify_signature(payload, signature, identity):
|
|
142
|
+
return None, identity, "unauthorized: invalid signature"
|
|
143
|
+
|
|
144
|
+
return prompt, identity, None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def get_agent_address(agent) -> str:
|
|
148
|
+
"""Generate deterministic address from agent name."""
|
|
149
|
+
h = hashlib.sha256(agent.name.encode()).hexdigest()
|
|
150
|
+
return f"0x{h[:40]}"
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def evaluate_with_trust_agent(trust_agent, prompt: str, identity: str, sig_valid: bool) -> tuple[bool, str]:
|
|
154
|
+
"""Evaluate request using a custom trust agent (policy or Agent).
|
|
155
|
+
|
|
156
|
+
Only called when trust is a policy string or custom Agent - NOT for simple levels.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
trust_agent: The trust agent created from policy or custom Agent
|
|
160
|
+
prompt: The request prompt
|
|
161
|
+
identity: The requester's identity/address
|
|
162
|
+
sig_valid: Whether the signature is valid
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
(accepted, reason) tuple
|
|
166
|
+
"""
|
|
167
|
+
from pydantic import BaseModel
|
|
168
|
+
from ...llm_do import llm_do
|
|
169
|
+
|
|
170
|
+
class TrustDecision(BaseModel):
|
|
171
|
+
accept: bool
|
|
172
|
+
reason: str
|
|
173
|
+
|
|
174
|
+
request_info = f"""Evaluate this request:
|
|
175
|
+
- prompt: {prompt[:200]}{'...' if len(prompt) > 200 else ''}
|
|
176
|
+
- identity: {identity or 'anonymous'}
|
|
177
|
+
- signature_valid: {sig_valid}"""
|
|
178
|
+
|
|
179
|
+
decision = llm_do(
|
|
180
|
+
request_info,
|
|
181
|
+
output=TrustDecision,
|
|
182
|
+
system_prompt=trust_agent.system_prompt,
|
|
183
|
+
)
|
|
184
|
+
return decision.accept, decision.reason
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def is_custom_trust(trust) -> bool:
|
|
188
|
+
"""Check if trust needs a custom agent (policy or Agent, not a level)."""
|
|
189
|
+
if not isinstance(trust, str):
|
|
190
|
+
return True # It's an Agent
|
|
191
|
+
return trust not in TRUST_LEVELS # It's a policy string
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: HTTP/WebSocket route handlers for agent hosting endpoints
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [network/host/session.py, connectonion/__init__.py] | imported by [network/host/server.py, network/asgi/http.py, network/asgi/websocket.py] | tested by [tests/network/test_host_routes.py]
|
|
5
|
+
Data flow: receives HTTP request → input_handler() calls create_agent() for fresh instance → server generates session_id (new) or uses existing (continuation) → calls agent.input(prompt, session) → stores result in SessionStorage → returns {session_id, status, result, duration_ms, session}
|
|
6
|
+
State/Effects: reads/writes SessionStorage via storage.save()/get()/list() | factory creates fresh agent per request (prevents state bleeding) | writes session records with TTL expiry | session_id always server-generated
|
|
7
|
+
Integration: exposes input_handler(create_agent, storage, prompt, result_ttl, session, connection), session_handler(storage, session_id), sessions_handler(storage), health_handler(agent_name, start_time), info_handler(agent_metadata, trust), admin_logs_handler(agent_name), admin_sessions_handler() | used by server.py and ASGI adapters
|
|
8
|
+
Performance: factory creates fresh agent per request (thread-safe, no deep copy issues) | SessionStorage TTL auto-cleanup | session continuation via session dict (multi-turn conversations)
|
|
9
|
+
Errors: session_handler returns None if session_id not found | admin_logs_handler returns {"error": "..."} if log file missing
|
|
10
|
+
Route handlers for hosted agents.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import time
|
|
14
|
+
import uuid
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Callable
|
|
17
|
+
|
|
18
|
+
from .session import Session, SessionStorage
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def input_handler(create_agent: Callable, storage: SessionStorage, prompt: str, result_ttl: int,
|
|
22
|
+
session: dict | None = None, connection=None) -> dict:
|
|
23
|
+
"""POST /input (and WebSocket /ws)
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
create_agent: Factory function that returns a fresh Agent instance
|
|
27
|
+
storage: SessionStorage for persisting results
|
|
28
|
+
prompt: The user's prompt
|
|
29
|
+
result_ttl: How long to keep the result on server
|
|
30
|
+
session: Optional conversation session for continuation
|
|
31
|
+
connection: WebSocket connection for bidirectional I/O (None for HTTP)
|
|
32
|
+
"""
|
|
33
|
+
agent = create_agent() # Fresh instance per request
|
|
34
|
+
agent.io = connection # WebSocket connection or None for HTTP
|
|
35
|
+
now = time.time()
|
|
36
|
+
|
|
37
|
+
# Server generates session_id (new session) or uses existing (continuation)
|
|
38
|
+
# NOTE: We mutate `session` dict here, but it's safe - the dict is created
|
|
39
|
+
# fresh from json.loads() per request, not shared anywhere.
|
|
40
|
+
session = session or {}
|
|
41
|
+
session_id = session.get('session_id') or str(uuid.uuid4())
|
|
42
|
+
session['session_id'] = session_id
|
|
43
|
+
|
|
44
|
+
# Create storage record
|
|
45
|
+
record = Session(
|
|
46
|
+
session_id=session_id,
|
|
47
|
+
status="running",
|
|
48
|
+
prompt=prompt,
|
|
49
|
+
created=now,
|
|
50
|
+
expires=now + result_ttl,
|
|
51
|
+
)
|
|
52
|
+
storage.save(record)
|
|
53
|
+
|
|
54
|
+
# TODO: If agent.input() throws, record stays "running" until TTL expires.
|
|
55
|
+
# This is acceptable: client gets 500 error, record expires naturally.
|
|
56
|
+
start = time.time()
|
|
57
|
+
result = agent.input(prompt, session=session)
|
|
58
|
+
duration_ms = int((time.time() - start) * 1000)
|
|
59
|
+
|
|
60
|
+
record.status = "done"
|
|
61
|
+
record.result = result
|
|
62
|
+
record.duration_ms = duration_ms
|
|
63
|
+
storage.save(record)
|
|
64
|
+
|
|
65
|
+
# Return result with updated session for client to continue conversation
|
|
66
|
+
return {
|
|
67
|
+
"session_id": session_id,
|
|
68
|
+
"status": "done",
|
|
69
|
+
"result": result,
|
|
70
|
+
"duration_ms": duration_ms,
|
|
71
|
+
"session": agent.current_session
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def session_handler(storage: SessionStorage, session_id: str) -> dict | None:
|
|
76
|
+
"""GET /sessions/{id}"""
|
|
77
|
+
session = storage.get(session_id)
|
|
78
|
+
return session.model_dump() if session else None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def sessions_handler(storage: SessionStorage) -> dict:
|
|
82
|
+
"""GET /sessions"""
|
|
83
|
+
return {"sessions": [s.model_dump() for s in storage.list()]}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def health_handler(agent_name: str, start_time: float) -> dict:
|
|
87
|
+
"""GET /health"""
|
|
88
|
+
return {"status": "healthy", "agent": agent_name, "uptime": int(time.time() - start_time)}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def info_handler(agent_metadata: dict, trust: str) -> dict:
|
|
92
|
+
"""GET /info
|
|
93
|
+
|
|
94
|
+
Returns pre-extracted metadata - no agent creation needed.
|
|
95
|
+
"""
|
|
96
|
+
from ... import __version__
|
|
97
|
+
return {
|
|
98
|
+
"name": agent_metadata["name"],
|
|
99
|
+
"address": agent_metadata["address"],
|
|
100
|
+
"tools": agent_metadata["tools"],
|
|
101
|
+
"trust": trust,
|
|
102
|
+
"version": __version__,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def admin_logs_handler(agent_name: str) -> dict:
|
|
107
|
+
"""GET /admin/logs - return plain text activity log file."""
|
|
108
|
+
log_path = Path(f".co/logs/{agent_name}.log")
|
|
109
|
+
if log_path.exists():
|
|
110
|
+
return {"content": log_path.read_text()}
|
|
111
|
+
return {"error": "No logs found"}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def admin_sessions_handler() -> dict:
|
|
115
|
+
"""GET /admin/sessions - return raw session YAML files as JSON.
|
|
116
|
+
|
|
117
|
+
Returns session files as-is (converted from YAML to JSON). Each session
|
|
118
|
+
contains: name, created, updated, total_cost, total_tokens, turns array.
|
|
119
|
+
Frontend handles the display logic.
|
|
120
|
+
"""
|
|
121
|
+
import yaml
|
|
122
|
+
sessions_dir = Path(".co/evals")
|
|
123
|
+
if not sessions_dir.exists():
|
|
124
|
+
return {"sessions": []}
|
|
125
|
+
|
|
126
|
+
sessions = []
|
|
127
|
+
for session_file in sessions_dir.glob("*.yaml"):
|
|
128
|
+
with open(session_file) as f:
|
|
129
|
+
session_data = yaml.safe_load(f)
|
|
130
|
+
if session_data:
|
|
131
|
+
sessions.append(session_data)
|
|
132
|
+
|
|
133
|
+
# Sort by updated date descending (newest first)
|
|
134
|
+
sessions.sort(key=lambda s: s.get("updated", s.get("created", "")), reverse=True)
|
|
135
|
+
return {"sessions": sessions}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Host agent as HTTP/WebSocket server with trust-based access control
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [network/asgi/, network/trust/, network/host/session.py, network/host/auth.py, network/host/routes.py] | imported by [network/__init__.py as host()] | tested by [tests/network/test_host.py]
|
|
5
|
+
Data flow: host(create_agent, port, trust) → _create_route_handlers() wraps all routes → asgi_create_app() creates FastAPI/Starlette app → uvicorn.run() starts server → each request calls create_agent() for fresh instance → executes via input_handler()/ws_input() → returns result + session | trust enforcement via extract_and_authenticate() at request boundary
|
|
6
|
+
State/Effects: starts HTTP server on specified port | creates .co/logs/ directory | stores sessions in SessionStorage (in-memory with TTL) | optionally announces to relay server | each request gets fresh agent instance (no state bleeding)
|
|
7
|
+
Integration: exposes host(create_agent, port=8000, trust=None, result_ttl=3600, relay_url=None) | creates ASGI app with routes: POST /input, GET /sessions, GET /sessions/{id}, GET /health, GET /info, WebSocket /ws, admin endpoints | trust accepts: "open"/"careful"/"strict" (level), markdown string (policy), or Agent (custom verifier)
|
|
8
|
+
Performance: factory pattern creates fresh agent per request (thread-safe) | SessionStorage auto-expires old results via TTL | WebSocket supports real-time bidirectional I/O | relay connection runs in background thread
|
|
9
|
+
Errors: trust errors return 401/403 via extract_and_authenticate() | missing sessions return None (404) | raises if port already in use
|
|
10
|
+
Host an agent over HTTP/WebSocket.
|
|
11
|
+
|
|
12
|
+
Trust enforcement happens at the host level, not in the Agent.
|
|
13
|
+
This provides clean separation: Agent does work, host controls access.
|
|
14
|
+
|
|
15
|
+
Trust parameter accepts three forms:
|
|
16
|
+
1. Level (string): "open", "careful", "strict"
|
|
17
|
+
2. Policy (string): Natural language or file path
|
|
18
|
+
3. Agent: Custom Agent instance for verification
|
|
19
|
+
|
|
20
|
+
All forms create a trust agent behind the scenes.
|
|
21
|
+
|
|
22
|
+
Worker Isolation:
|
|
23
|
+
Each request calls the create_agent factory to get a fresh agent instance.
|
|
24
|
+
This ensures complete isolation - tools with state (like BrowserTool)
|
|
25
|
+
don't interfere between concurrent requests.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import os
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Callable, Union
|
|
31
|
+
|
|
32
|
+
from rich.console import Console
|
|
33
|
+
from rich.panel import Panel
|
|
34
|
+
|
|
35
|
+
from ..asgi import create_app as asgi_create_app
|
|
36
|
+
from ..trust import get_default_trust_level
|
|
37
|
+
from .session import SessionStorage
|
|
38
|
+
from .auth import extract_and_authenticate
|
|
39
|
+
from .routes import (
|
|
40
|
+
input_handler,
|
|
41
|
+
session_handler,
|
|
42
|
+
sessions_handler,
|
|
43
|
+
health_handler,
|
|
44
|
+
info_handler,
|
|
45
|
+
admin_logs_handler,
|
|
46
|
+
admin_sessions_handler,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_default_trust() -> str:
|
|
51
|
+
"""Get default trust based on environment.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Trust level based on CONNECTONION_ENV, defaults to 'careful'
|
|
55
|
+
"""
|
|
56
|
+
return get_default_trust_level() or "careful"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _extract_agent_metadata(create_agent: Callable) -> tuple[dict, object]:
|
|
60
|
+
"""Extract metadata from a sample agent instance.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
(metadata dict, sample_agent) - sample_agent for additional extraction
|
|
64
|
+
"""
|
|
65
|
+
sample = create_agent()
|
|
66
|
+
metadata = {
|
|
67
|
+
"name": sample.name,
|
|
68
|
+
"tools": sample.tools.names(),
|
|
69
|
+
}
|
|
70
|
+
return metadata, sample
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _create_route_handlers(create_agent: Callable, agent_metadata: dict, result_ttl: int):
|
|
74
|
+
"""Create route handler dict for ASGI app.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
create_agent: Factory function that returns a fresh Agent instance.
|
|
78
|
+
Called once per request for isolation.
|
|
79
|
+
agent_metadata: Pre-extracted metadata (name, tools, address) - avoids
|
|
80
|
+
creating agents for health/info endpoints.
|
|
81
|
+
result_ttl: How long to keep results on server in seconds
|
|
82
|
+
"""
|
|
83
|
+
agent_name = agent_metadata["name"]
|
|
84
|
+
|
|
85
|
+
def handle_input(storage, prompt, session=None, connection=None):
|
|
86
|
+
return input_handler(create_agent, storage, prompt, result_ttl, session, connection)
|
|
87
|
+
|
|
88
|
+
def handle_ws_input(storage, prompt, connection, session=None):
|
|
89
|
+
return input_handler(create_agent, storage, prompt, result_ttl, session, connection)
|
|
90
|
+
|
|
91
|
+
def handle_health(start_time):
|
|
92
|
+
return health_handler(agent_name, start_time)
|
|
93
|
+
|
|
94
|
+
def handle_info(trust):
|
|
95
|
+
return info_handler(agent_metadata, trust)
|
|
96
|
+
|
|
97
|
+
def handle_admin_logs():
|
|
98
|
+
return admin_logs_handler(agent_name)
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
"input": handle_input,
|
|
102
|
+
"session": session_handler,
|
|
103
|
+
"sessions": sessions_handler,
|
|
104
|
+
"health": handle_health,
|
|
105
|
+
"info": handle_info,
|
|
106
|
+
"auth": extract_and_authenticate,
|
|
107
|
+
"ws_input": handle_ws_input,
|
|
108
|
+
"admin_logs": handle_admin_logs,
|
|
109
|
+
"admin_sessions": admin_sessions_handler,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _start_relay_background(create_agent: Callable, relay_url: str, addr_data: dict, agent_summary: str):
|
|
114
|
+
"""Start relay connection in background thread.
|
|
115
|
+
|
|
116
|
+
The relay connection runs alongside the HTTP server, allowing the agent
|
|
117
|
+
to be discovered via P2P network while also serving HTTP requests.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
create_agent: Factory function that returns a fresh Agent instance
|
|
121
|
+
relay_url: WebSocket URL for P2P relay
|
|
122
|
+
addr_data: Agent address data (public key, address)
|
|
123
|
+
agent_summary: Summary text for relay announcement
|
|
124
|
+
"""
|
|
125
|
+
import asyncio
|
|
126
|
+
import threading
|
|
127
|
+
from .. import announce, relay
|
|
128
|
+
|
|
129
|
+
# Create ANNOUNCE message
|
|
130
|
+
announce_msg = announce.create_announce_message(addr_data, agent_summary, endpoints=[])
|
|
131
|
+
|
|
132
|
+
# Task handler - fresh instance for each request
|
|
133
|
+
# NOTE: agent.input() is synchronous inside async function, but this runs in
|
|
134
|
+
# a separate thread with its own event loop (not uvicorn's). Only blocks the
|
|
135
|
+
# relay thread, not the HTTP server. Could use asyncio.to_thread() if relay
|
|
136
|
+
# needs concurrent task handling or heartbeat during execution.
|
|
137
|
+
async def task_handler(prompt: str) -> str:
|
|
138
|
+
agent = create_agent()
|
|
139
|
+
return agent.input(prompt)
|
|
140
|
+
|
|
141
|
+
async def relay_loop():
|
|
142
|
+
ws = await relay.connect(relay_url)
|
|
143
|
+
await relay.serve_loop(ws, announce_msg, task_handler)
|
|
144
|
+
|
|
145
|
+
def run():
|
|
146
|
+
asyncio.run(relay_loop())
|
|
147
|
+
|
|
148
|
+
thread = threading.Thread(target=run, daemon=True, name="relay-connection")
|
|
149
|
+
thread.start()
|
|
150
|
+
return thread
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def host(
|
|
154
|
+
create_agent: Callable,
|
|
155
|
+
port: int = None,
|
|
156
|
+
trust: Union[str, "Agent"] = "careful",
|
|
157
|
+
result_ttl: int = 86400,
|
|
158
|
+
workers: int = 1,
|
|
159
|
+
reload: bool = False,
|
|
160
|
+
*,
|
|
161
|
+
relay_url: str = "wss://oo.openonion.ai",
|
|
162
|
+
blacklist: list | None = None,
|
|
163
|
+
whitelist: list | None = None,
|
|
164
|
+
):
|
|
165
|
+
"""
|
|
166
|
+
Host an agent over HTTP/WebSocket with optional P2P relay discovery.
|
|
167
|
+
|
|
168
|
+
Each request calls create_agent() to get a fresh Agent instance.
|
|
169
|
+
This ensures complete isolation between concurrent requests.
|
|
170
|
+
|
|
171
|
+
State Control via Closure:
|
|
172
|
+
# Isolated state (default, safest) - create inside:
|
|
173
|
+
def create_agent():
|
|
174
|
+
browser = BrowserTool() # Fresh per request
|
|
175
|
+
return Agent("assistant", tools=[browser])
|
|
176
|
+
|
|
177
|
+
# Shared state (advanced) - create outside, capture via closure:
|
|
178
|
+
browser = BrowserTool() # Shared across all requests
|
|
179
|
+
def create_agent():
|
|
180
|
+
return Agent("assistant", tools=[browser])
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
create_agent: Function that returns a fresh Agent instance.
|
|
184
|
+
Called once per request. Define tools inside for isolation,
|
|
185
|
+
or outside for shared state.
|
|
186
|
+
port: HTTP port (default: PORT env var or 8000)
|
|
187
|
+
trust: Trust level, policy, or Agent:
|
|
188
|
+
- Level: "open", "careful", "strict"
|
|
189
|
+
- Policy: Natural language or file path
|
|
190
|
+
- Agent: Custom trust agent
|
|
191
|
+
result_ttl: How long to keep results on server in seconds (default 24h)
|
|
192
|
+
workers: Number of worker processes
|
|
193
|
+
reload: Auto-reload on code changes
|
|
194
|
+
relay_url: P2P relay URL (default: production relay)
|
|
195
|
+
- Set to None to disable relay
|
|
196
|
+
blacklist: Blocked identities
|
|
197
|
+
whitelist: Allowed identities
|
|
198
|
+
|
|
199
|
+
Endpoints:
|
|
200
|
+
POST /input - Submit prompt, get result
|
|
201
|
+
GET /sessions/{id} - Get session by ID
|
|
202
|
+
GET /sessions - List all sessions
|
|
203
|
+
GET /health - Health check
|
|
204
|
+
GET /info - Agent info
|
|
205
|
+
WS /ws - WebSocket
|
|
206
|
+
GET /logs - Activity log (requires OPENONION_API_KEY)
|
|
207
|
+
GET /logs/sessions - Activity sessions (requires OPENONION_API_KEY)
|
|
208
|
+
"""
|
|
209
|
+
import uvicorn
|
|
210
|
+
from ... import address
|
|
211
|
+
|
|
212
|
+
# Use PORT env var if port not specified (for container deployments)
|
|
213
|
+
if port is None:
|
|
214
|
+
port = int(os.environ.get("PORT", 8000))
|
|
215
|
+
|
|
216
|
+
# Extract metadata once at startup
|
|
217
|
+
agent_metadata, sample = _extract_agent_metadata(create_agent)
|
|
218
|
+
agent_summary = sample.system_prompt[:1000] if sample.system_prompt else f"{agent_metadata['name']} agent"
|
|
219
|
+
|
|
220
|
+
# Load or generate agent identity
|
|
221
|
+
co_dir = Path.cwd() / '.co'
|
|
222
|
+
addr_data = address.load(co_dir)
|
|
223
|
+
|
|
224
|
+
if addr_data is None:
|
|
225
|
+
addr_data = address.generate()
|
|
226
|
+
address.save(addr_data, co_dir)
|
|
227
|
+
|
|
228
|
+
agent_metadata["address"] = addr_data['address']
|
|
229
|
+
|
|
230
|
+
storage = SessionStorage()
|
|
231
|
+
route_handlers = _create_route_handlers(create_agent, agent_metadata, result_ttl)
|
|
232
|
+
app = asgi_create_app(
|
|
233
|
+
route_handlers=route_handlers,
|
|
234
|
+
storage=storage,
|
|
235
|
+
trust=trust,
|
|
236
|
+
blacklist=blacklist,
|
|
237
|
+
whitelist=whitelist,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Start relay connection in background (if enabled)
|
|
241
|
+
if relay_url:
|
|
242
|
+
_start_relay_background(create_agent, relay_url, addr_data, agent_summary)
|
|
243
|
+
|
|
244
|
+
# Display startup info
|
|
245
|
+
relay_status = f"[green]✓[/] {relay_url}" if relay_url else "[dim]disabled[/]"
|
|
246
|
+
Console().print(Panel(
|
|
247
|
+
f"[bold]POST[/] http://localhost:{port}/input\n"
|
|
248
|
+
f"[dim]GET /sessions/{{id}} · /sessions · /health · /info[/]\n"
|
|
249
|
+
f"[dim]WS ws://localhost:{port}/ws\n"
|
|
250
|
+
f"[dim]UI http://localhost:{port}/docs[/]\n\n"
|
|
251
|
+
f"[bold]Address:[/] {agent_metadata['address']}\n"
|
|
252
|
+
f"[bold]Relay:[/] {relay_status}",
|
|
253
|
+
title=f"[green]Agent '{agent_metadata['name']}'[/]"
|
|
254
|
+
))
|
|
255
|
+
|
|
256
|
+
uvicorn.run(app, host="0.0.0.0", port=port, workers=workers, reload=reload, log_level="warning")
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def create_app(create_agent: Callable, storage=None, trust="careful", result_ttl=86400, *, blacklist=None, whitelist=None):
|
|
260
|
+
"""Create ASGI app for external uvicorn/gunicorn usage.
|
|
261
|
+
|
|
262
|
+
Each request calls create_agent() to get a fresh Agent instance.
|
|
263
|
+
|
|
264
|
+
Usage:
|
|
265
|
+
from connectonion.network import create_app
|
|
266
|
+
|
|
267
|
+
def create_agent():
|
|
268
|
+
return Agent("assistant", tools=[search])
|
|
269
|
+
|
|
270
|
+
app = create_app(create_agent)
|
|
271
|
+
# uvicorn myagent:app --workers 4
|
|
272
|
+
"""
|
|
273
|
+
from .auth import get_agent_address
|
|
274
|
+
|
|
275
|
+
if storage is None:
|
|
276
|
+
storage = SessionStorage()
|
|
277
|
+
|
|
278
|
+
# Extract metadata once at startup
|
|
279
|
+
agent_metadata, sample = _extract_agent_metadata(create_agent)
|
|
280
|
+
agent_metadata["address"] = get_agent_address(sample)
|
|
281
|
+
|
|
282
|
+
route_handlers = _create_route_handlers(create_agent, agent_metadata, result_ttl)
|
|
283
|
+
return asgi_create_app(
|
|
284
|
+
route_handlers=route_handlers,
|
|
285
|
+
storage=storage,
|
|
286
|
+
trust=trust,
|
|
287
|
+
blacklist=blacklist,
|
|
288
|
+
whitelist=whitelist,
|
|
289
|
+
)
|