iCode-TUI 0.29.1__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.
- chrys/__init__.py +16 -0
- chrys/app/__init__.py +3 -0
- chrys/app/acp/__init__.py +3 -0
- chrys/app/acp/bridge.py +664 -0
- chrys/app/acp/content.py +132 -0
- chrys/app/acp/doc/frontend-api.md +425 -0
- chrys/app/acp/history.py +585 -0
- chrys/app/acp/server.py +1796 -0
- chrys/app/acp/session_manager.py +1815 -0
- chrys/app/acp/tool_status.py +45 -0
- chrys/app/cli/__init__.py +3 -0
- chrys/app/cli/acp.py +178 -0
- chrys/app/cli/app.py +165 -0
- chrys/app/cli/approvals.py +101 -0
- chrys/app/cli/headless.py +223 -0
- chrys/app/cli/launch_cwd.py +42 -0
- chrys/app/cli/profiles.py +298 -0
- chrys/app/cli/progress.py +857 -0
- chrys/app/cli/run.py +425 -0
- chrys/app/cli/serve.py +1593 -0
- chrys/app/cli/tool_summary.py +225 -0
- chrys/app/cli/trajectory.py +201 -0
- chrys/app/cli/workflow.py +601 -0
- chrys/app/features/__init__.py +3 -0
- chrys/app/features/buddy/__init__.py +3 -0
- chrys/app/features/buddy/actions.py +68 -0
- chrys/app/features/buddy/animation.py +76 -0
- chrys/app/features/buddy/commands.py +196 -0
- chrys/app/features/buddy/docs/design.md +187 -0
- chrys/app/features/buddy/hatchery.py +80 -0
- chrys/app/features/buddy/lifecycle.py +41 -0
- chrys/app/features/buddy/model.py +265 -0
- chrys/app/features/buddy/pixel_renderer.py +113 -0
- chrys/app/features/buddy/pixel_sprites.py +279 -0
- chrys/app/features/buddy/portrait.py +76 -0
- chrys/app/features/buddy/progression.py +58 -0
- chrys/app/features/buddy/replies.py +191 -0
- chrys/app/features/buddy/sprites/alien.toml +70 -0
- chrys/app/features/buddy/sprites/axolotl.toml +71 -0
- chrys/app/features/buddy/sprites/bat.toml +69 -0
- chrys/app/features/buddy/sprites/bee.toml +69 -0
- chrys/app/features/buddy/sprites/cactus.toml +73 -0
- chrys/app/features/buddy/sprites/capybara.toml +70 -0
- chrys/app/features/buddy/sprites/cat.toml +69 -0
- chrys/app/features/buddy/sprites/crab.toml +70 -0
- chrys/app/features/buddy/sprites/dragon.toml +71 -0
- chrys/app/features/buddy/sprites/duck.toml +69 -0
- chrys/app/features/buddy/sprites/fox.toml +69 -0
- chrys/app/features/buddy/sprites/frog.toml +70 -0
- chrys/app/features/buddy/sprites/ghost.toml +70 -0
- chrys/app/features/buddy/sprites/goose.toml +69 -0
- chrys/app/features/buddy/sprites/jelly.toml +70 -0
- chrys/app/features/buddy/sprites/llama.toml +70 -0
- chrys/app/features/buddy/sprites/mushroom.toml +69 -0
- chrys/app/features/buddy/sprites/octopus.toml +70 -0
- chrys/app/features/buddy/sprites/owl.toml +70 -0
- chrys/app/features/buddy/sprites/panda.toml +71 -0
- chrys/app/features/buddy/sprites/penguin.toml +69 -0
- chrys/app/features/buddy/sprites/rabbit.toml +70 -0
- chrys/app/features/buddy/sprites/robot.toml +72 -0
- chrys/app/features/buddy/sprites/shark.toml +70 -0
- chrys/app/features/buddy/sprites/snail.toml +69 -0
- chrys/app/features/buddy/sprites/snake.toml +69 -0
- chrys/app/features/buddy/sprites/tabby.toml +69 -0
- chrys/app/features/buddy/sprites/turtle.toml +69 -0
- chrys/app/features/buddy/store.py +143 -0
- chrys/app/features/session_title/__init__.py +7 -0
- chrys/app/features/session_title/updater.py +582 -0
- chrys/app/installer.py +651 -0
- chrys/app/parsing.py +17 -0
- chrys/app/tui/__init__.py +3 -0
- chrys/app/tui/app.py +1287 -0
- chrys/app/tui/behaviors/__init__.py +3 -0
- chrys/app/tui/behaviors/click_outside_dismiss.py +90 -0
- chrys/app/tui/behaviors/insert_clipboard.py +73 -0
- chrys/app/tui/behaviors/right_click_copy.py +146 -0
- chrys/app/tui/binding_display.py +139 -0
- chrys/app/tui/buddy_messages.py +17 -0
- chrys/app/tui/buddy_reply.py +86 -0
- chrys/app/tui/chrys.tcss +549 -0
- chrys/app/tui/clipboard.py +109 -0
- chrys/app/tui/copy_messages.py +10 -0
- chrys/app/tui/i18n.py +230 -0
- chrys/app/tui/language.py +58 -0
- chrys/app/tui/notifications/__init__.py +12 -0
- chrys/app/tui/notifications/drivers.py +180 -0
- chrys/app/tui/notifications/service.py +126 -0
- chrys/app/tui/notifications/settings.py +141 -0
- chrys/app/tui/screens/__init__.py +3 -0
- chrys/app/tui/screens/agents/__init__.py +8 -0
- chrys/app/tui/screens/agents/agent_draft_store.py +937 -0
- chrys/app/tui/screens/agents/config.py +2142 -0
- chrys/app/tui/screens/agents/config.tcss +169 -0
- chrys/app/tui/screens/agents/panels/__init__.py +3 -0
- chrys/app/tui/screens/agents/panels/acp.py +1176 -0
- chrys/app/tui/screens/agents/panels/basic.py +497 -0
- chrys/app/tui/screens/agents/panels/compaction.py +231 -0
- chrys/app/tui/screens/agents/panels/config_card.py +218 -0
- chrys/app/tui/screens/agents/panels/instructions.py +91 -0
- chrys/app/tui/screens/agents/panels/markdown_report.py +76 -0
- chrys/app/tui/screens/agents/panels/mcp.py +2262 -0
- chrys/app/tui/screens/agents/panels/memory.py +684 -0
- chrys/app/tui/screens/agents/panels/path_entry.py +227 -0
- chrys/app/tui/screens/agents/panels/skills.py +806 -0
- chrys/app/tui/screens/agents/panels/subagents.py +652 -0
- chrys/app/tui/screens/agents/panels/tools.py +205 -0
- chrys/app/tui/screens/agents/picker.py +103 -0
- chrys/app/tui/screens/agents/skill_paths.py +38 -0
- chrys/app/tui/screens/agents/validation_messages.py +452 -0
- chrys/app/tui/screens/dialogs/__init__.py +35 -0
- chrys/app/tui/screens/dialogs/agent_load.py +834 -0
- chrys/app/tui/screens/dialogs/agent_load.tcss +78 -0
- chrys/app/tui/screens/dialogs/app_mode.py +55 -0
- chrys/app/tui/screens/dialogs/approval/__init__.py +15 -0
- chrys/app/tui/screens/dialogs/approval/approval.tcss +311 -0
- chrys/app/tui/screens/dialogs/approval/bodies/__init__.py +3 -0
- chrys/app/tui/screens/dialogs/approval/bodies/file_edit.py +210 -0
- chrys/app/tui/screens/dialogs/approval/bodies/sub_agent.py +82 -0
- chrys/app/tui/screens/dialogs/approval/body.py +110 -0
- chrys/app/tui/screens/dialogs/approval/dialog.py +565 -0
- chrys/app/tui/screens/dialogs/approval/mode.py +105 -0
- chrys/app/tui/screens/dialogs/ask_user.py +148 -0
- chrys/app/tui/screens/dialogs/ask_user.tcss +62 -0
- chrys/app/tui/screens/dialogs/base.py +84 -0
- chrys/app/tui/screens/dialogs/confirm.py +184 -0
- chrys/app/tui/screens/dialogs/confirm.tcss +46 -0
- chrys/app/tui/screens/dialogs/connection_test.py +183 -0
- chrys/app/tui/screens/dialogs/connection_test.tcss +92 -0
- chrys/app/tui/screens/dialogs/diagram.py +102 -0
- chrys/app/tui/screens/dialogs/diagram.tcss +24 -0
- chrys/app/tui/screens/dialogs/editor.py +378 -0
- chrys/app/tui/screens/dialogs/editor.tcss +132 -0
- chrys/app/tui/screens/dialogs/file_picker.py +582 -0
- chrys/app/tui/screens/dialogs/file_picker.tcss +78 -0
- chrys/app/tui/screens/dialogs/fork_session.py +219 -0
- chrys/app/tui/screens/dialogs/fork_session.tcss +75 -0
- chrys/app/tui/screens/dialogs/image_compression.py +102 -0
- chrys/app/tui/screens/dialogs/image_compression.tcss +28 -0
- chrys/app/tui/screens/dialogs/man_page.py +172 -0
- chrys/app/tui/screens/dialogs/man_page.tcss +39 -0
- chrys/app/tui/screens/dialogs/new_folder.py +277 -0
- chrys/app/tui/screens/dialogs/new_folder.tcss +51 -0
- chrys/app/tui/screens/dialogs/notice.tcss +14 -0
- chrys/app/tui/screens/dialogs/prompt_cache.py +144 -0
- chrys/app/tui/screens/dialogs/prompt_cache.tcss +38 -0
- chrys/app/tui/screens/dialogs/runtime_details.py +371 -0
- chrys/app/tui/screens/dialogs/runtime_details.tcss +49 -0
- chrys/app/tui/screens/dialogs/session_title.py +110 -0
- chrys/app/tui/screens/dialogs/session_title.tcss +43 -0
- chrys/app/tui/screens/dialogs/tool_view.py +207 -0
- chrys/app/tui/screens/dialogs/tool_view.tcss +43 -0
- chrys/app/tui/screens/dialogs/vision_unsupported.py +122 -0
- chrys/app/tui/screens/dialogs/vision_unsupported.tcss +35 -0
- chrys/app/tui/screens/dialogs/workflow_confirm.py +91 -0
- chrys/app/tui/screens/dialogs/workflow_confirm.tcss +17 -0
- chrys/app/tui/screens/dialogs/workflow_input.py +258 -0
- chrys/app/tui/screens/dialogs/workflow_input.tcss +73 -0
- chrys/app/tui/screens/dialogs/workflow_node.py +462 -0
- chrys/app/tui/screens/dialogs/workflow_node.tcss +41 -0
- chrys/app/tui/screens/dialogs/workflow_picker.py +46 -0
- chrys/app/tui/screens/dialogs/workflow_result.py +136 -0
- chrys/app/tui/screens/dialogs/workflow_result.tcss +14 -0
- chrys/app/tui/screens/diff/__init__.py +22 -0
- chrys/app/tui/screens/diff/rollback_modal.py +1381 -0
- chrys/app/tui/screens/diff/rollback_modal.tcss +248 -0
- chrys/app/tui/screens/diff/screen.py +1532 -0
- chrys/app/tui/screens/diff/screen.tcss +104 -0
- chrys/app/tui/screens/guides/__init__.py +7 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/agents.md +178 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/approval.md +117 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/chat-options.md +276 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/compaction.md +42 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/memory.md +48 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/models.md +158 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/settings.md +210 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/themes.md +61 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/tools.md +60 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/web-tools.md +286 -0
- chrys/app/tui/screens/guides/_docs/en/guides/daily-use/sessions.md +77 -0
- chrys/app/tui/screens/guides/_docs/en/guides/daily-use/workspaces.md +51 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/external-acp-agents.md +126 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/hooks.md +167 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/mcp.md +95 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/skills.md +175 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-acp.md +61 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-run.md +190 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-serve.md +239 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/workflows.md +664 -0
- chrys/app/tui/screens/guides/_docs/en/reference/agent-profile.md +548 -0
- chrys/app/tui/screens/guides/_docs/en/reference/hooks.md +602 -0
- chrys/app/tui/screens/guides/_docs/en/reference/opentelemetry.md +177 -0
- chrys/app/tui/screens/guides/_docs/en/reference/settings.md +245 -0
- chrys/app/tui/screens/guides/_docs/en/reference/themes.md +149 -0
- chrys/app/tui/screens/guides/_docs/en/reference/tool-kinds-and-names.md +58 -0
- chrys/app/tui/screens/guides/_docs/en/reference/tui-slash-commands.md +76 -0
- chrys/app/tui/screens/guides/_docs/en/reference/workflows.md +1006 -0
- chrys/app/tui/screens/guides/_docs/en/start/getting-started.md +170 -0
- chrys/app/tui/screens/guides/_docs/en/start/what-is-icode.md +34 -0
- chrys/app/tui/screens/guides/_docs/index.yaml +100 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/agents.md +178 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/approval.md +117 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/chat-options.md +276 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/compaction.md +42 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/memory.md +48 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/models.md +158 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/settings.md +210 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/themes.md +61 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/tools.md +60 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/web-tools.md +286 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/sessions.md +77 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/workspaces.md +51 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/external-acp-agents.md +126 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/hooks.md +167 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/mcp.md +95 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/skills.md +175 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-acp.md +61 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-run.md +190 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-serve.md +239 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/workflows.md +664 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/agent-profile.md +548 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/hooks.md +602 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/opentelemetry.md +177 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/settings.md +245 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/themes.md +149 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tool-kinds-and-names.md +58 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tui-slash-commands.md +76 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/workflows.md +1005 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/start/getting-started.md +170 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/start/what-is-icode.md +34 -0
- chrys/app/tui/screens/guides/index.py +215 -0
- chrys/app/tui/screens/guides/reader.py +173 -0
- chrys/app/tui/screens/guides/screen.py +442 -0
- chrys/app/tui/screens/guides/screen.tcss +52 -0
- chrys/app/tui/screens/logs/__init__.py +7 -0
- chrys/app/tui/screens/logs/screen.py +504 -0
- chrys/app/tui/screens/logs/screen.tcss +45 -0
- chrys/app/tui/screens/main/__init__.py +7 -0
- chrys/app/tui/screens/main/action_availability.py +30 -0
- chrys/app/tui/screens/main/buddy_command.py +143 -0
- chrys/app/tui/screens/main/commands.py +1073 -0
- chrys/app/tui/screens/main/config_actions.py +464 -0
- chrys/app/tui/screens/main/copy_actions.py +117 -0
- chrys/app/tui/screens/main/dialog_controllers.py +826 -0
- chrys/app/tui/screens/main/dialog_gateway.py +166 -0
- chrys/app/tui/screens/main/diff_controller.py +478 -0
- chrys/app/tui/screens/main/engine_read_model.py +184 -0
- chrys/app/tui/screens/main/event_handlers.py +2014 -0
- chrys/app/tui/screens/main/input_flow.py +241 -0
- chrys/app/tui/screens/main/live_diff.py +244 -0
- chrys/app/tui/screens/main/main_screen_presenter.py +295 -0
- chrys/app/tui/screens/main/model_indicator.py +217 -0
- chrys/app/tui/screens/main/navigation.py +343 -0
- chrys/app/tui/screens/main/ports.py +571 -0
- chrys/app/tui/screens/main/recent_dirs.py +123 -0
- chrys/app/tui/screens/main/rollback_controller.py +586 -0
- chrys/app/tui/screens/main/runtime_info.py +71 -0
- chrys/app/tui/screens/main/screen.py +2265 -0
- chrys/app/tui/screens/main/screen.tcss +21 -0
- chrys/app/tui/screens/main/session_handlers.py +1127 -0
- chrys/app/tui/screens/main/session_title.py +239 -0
- chrys/app/tui/screens/main/settings_coordinator.py +365 -0
- chrys/app/tui/screens/main/settings_persistence.py +184 -0
- chrys/app/tui/screens/main/shell_mode.py +112 -0
- chrys/app/tui/screens/main/state.py +289 -0
- chrys/app/tui/screens/main/submit_state.py +11 -0
- chrys/app/tui/screens/main/subscriptions.py +177 -0
- chrys/app/tui/screens/main/suggestions.py +982 -0
- chrys/app/tui/screens/main/tool_action_bridge.py +75 -0
- chrys/app/tui/screens/main/view_adapter.py +1567 -0
- chrys/app/tui/screens/main/workflow_browser.py +747 -0
- chrys/app/tui/screens/main/workflow_content.py +248 -0
- chrys/app/tui/screens/main/workflow_controller.py +240 -0
- chrys/app/tui/screens/main/workflow_feedback.py +137 -0
- chrys/app/tui/screens/main/workflow_flow.py +49 -0
- chrys/app/tui/screens/main/workflow_run_control.py +456 -0
- chrys/app/tui/screens/main/workflow_session_view.py +316 -0
- chrys/app/tui/screens/main/workspace_actions.py +272 -0
- chrys/app/tui/screens/main/workspace_branch.py +194 -0
- chrys/app/tui/screens/menus/__init__.py +8 -0
- chrys/app/tui/screens/menus/languages.py +114 -0
- chrys/app/tui/screens/models/__init__.py +7 -0
- chrys/app/tui/screens/models/picker.py +147 -0
- chrys/app/tui/screens/models/screen.py +2043 -0
- chrys/app/tui/screens/models/screen.tcss +255 -0
- chrys/app/tui/screens/sessions/__init__.py +8 -0
- chrys/app/tui/screens/sessions/presenter.py +586 -0
- chrys/app/tui/screens/sessions/screen.py +779 -0
- chrys/app/tui/screens/sessions/screen.tcss +127 -0
- chrys/app/tui/screens/settings/__init__.py +29 -0
- chrys/app/tui/screens/settings/danger.py +26 -0
- chrys/app/tui/screens/settings/dialog.py +281 -0
- chrys/app/tui/screens/settings/layout.py +519 -0
- chrys/app/tui/screens/settings/migrate_dialog.py +253 -0
- chrys/app/tui/screens/settings/panes/__init__.py +3 -0
- chrys/app/tui/screens/settings/panes/notifications.py +248 -0
- chrys/app/tui/screens/settings/panes/sessions.py +148 -0
- chrys/app/tui/screens/settings/ports.py +55 -0
- chrys/app/tui/screens/settings/provenance.py +102 -0
- chrys/app/tui/screens/settings/rows.py +546 -0
- chrys/app/tui/screens/settings/settings.tcss +406 -0
- chrys/app/tui/screens/themes/__init__.py +8 -0
- chrys/app/tui/screens/themes/dialogs.py +330 -0
- chrys/app/tui/screens/themes/editor.py +632 -0
- chrys/app/tui/screens/themes/editor.tcss +268 -0
- chrys/app/tui/screens/themes/messages.py +95 -0
- chrys/app/tui/screens/themes/palette.py +694 -0
- chrys/app/tui/screens/themes/panel.py +113 -0
- chrys/app/tui/screens/themes/picker.py +203 -0
- chrys/app/tui/screens/themes/save.py +191 -0
- chrys/app/tui/support/__init__.py +3 -0
- chrys/app/tui/support/file_manager.py +64 -0
- chrys/app/tui/support/gc_freeze.py +1114 -0
- chrys/app/tui/support/path_shortcuts.py +50 -0
- chrys/app/tui/support/prompt_history.py +298 -0
- chrys/app/tui/support/workspace_mru.py +476 -0
- chrys/app/tui/terminal/__init__.py +6 -0
- chrys/app/tui/terminal/_pty_backend.py +114 -0
- chrys/app/tui/terminal/emulator/__init__.py +24 -0
- chrys/app/tui/terminal/emulator/buffer.py +456 -0
- chrys/app/tui/terminal/emulator/charsets.py +113 -0
- chrys/app/tui/terminal/emulator/core.py +963 -0
- chrys/app/tui/terminal/emulator/input.py +342 -0
- chrys/app/tui/terminal/emulator/parser.py +227 -0
- chrys/app/tui/terminal/emulator/pen.py +158 -0
- chrys/app/tui/terminal/launcher.py +455 -0
- chrys/app/tui/terminal/leaked_reports.py +44 -0
- chrys/app/tui/terminal/panel.py +323 -0
- chrys/app/tui/terminal/pty_process.py +405 -0
- chrys/app/tui/terminal/rendering.py +151 -0
- chrys/app/tui/terminal/shell.py +273 -0
- chrys/app/tui/terminal/shell_integration.py +199 -0
- chrys/app/tui/terminal/title.py +224 -0
- chrys/app/tui/terminal/widget.py +654 -0
- chrys/app/tui/theme.py +422 -0
- chrys/app/tui/theme_loader.py +201 -0
- chrys/app/tui/theme_stylesheet.py +49 -0
- chrys/app/tui/themes/__init__.py +3 -0
- chrys/app/tui/themes/document.py +120 -0
- chrys/app/tui/themes/preview.py +170 -0
- chrys/app/tui/themes/store.py +176 -0
- chrys/app/tui/toasts.py +69 -0
- chrys/app/tui/util/__init__.py +3 -0
- chrys/app/tui/util/context_pressure.py +146 -0
- chrys/app/tui/util/debug_log.py +292 -0
- chrys/app/tui/util/diff_entries.py +64 -0
- chrys/app/tui/util/formatting.py +53 -0
- chrys/app/tui/util/git_branch.py +326 -0
- chrys/app/tui/util/invocation_progress.py +57 -0
- chrys/app/tui/util/logo.py +32 -0
- chrys/app/tui/util/message_gate.py +35 -0
- chrys/app/tui/util/mutation_sources.py +14 -0
- chrys/app/tui/util/picker_text.py +27 -0
- chrys/app/tui/util/removal.py +47 -0
- chrys/app/tui/util/rich_style.py +36 -0
- chrys/app/tui/util/source_text.py +12 -0
- chrys/app/tui/util/static_update.py +88 -0
- chrys/app/tui/util/todo_format.py +51 -0
- chrys/app/tui/util/visibility.py +214 -0
- chrys/app/tui/widgets/__init__.py +60 -0
- chrys/app/tui/widgets/ask_user_controls.py +652 -0
- chrys/app/tui/widgets/ask_user_markdown.py +17 -0
- chrys/app/tui/widgets/ask_user_prompt.py +606 -0
- chrys/app/tui/widgets/buttons.py +52 -0
- chrys/app/tui/widgets/chat/__init__.py +3 -0
- chrys/app/tui/widgets/chat/agent_transcript_surface.py +818 -0
- chrys/app/tui/widgets/chat/chrome.py +296 -0
- chrys/app/tui/widgets/chat/compaction_card.py +353 -0
- chrys/app/tui/widgets/chat/context_fold.py +77 -0
- chrys/app/tui/widgets/chat/file_snapshot.py +91 -0
- chrys/app/tui/widgets/chat/image_preview.py +214 -0
- chrys/app/tui/widgets/chat/live_renderer.py +383 -0
- chrys/app/tui/widgets/chat/messages.py +1039 -0
- chrys/app/tui/widgets/chat/panel.py +1769 -0
- chrys/app/tui/widgets/chat/ports.py +245 -0
- chrys/app/tui/widgets/chat/renderers/__init__.py +7 -0
- chrys/app/tui/widgets/chat/renderers/ask_user.py +747 -0
- chrys/app/tui/widgets/chat/renderers/execute.py +431 -0
- chrys/app/tui/widgets/chat/renderers/file_edit.py +925 -0
- chrys/app/tui/widgets/chat/renderers/hosted_code.py +83 -0
- chrys/app/tui/widgets/chat/renderers/hosted_generic.py +330 -0
- chrys/app/tui/widgets/chat/renderers/hosted_image.py +107 -0
- chrys/app/tui/widgets/chat/renderers/hosted_mcp.py +43 -0
- chrys/app/tui/widgets/chat/renderers/hosted_search.py +167 -0
- chrys/app/tui/widgets/chat/renderers/hosted_shell.py +132 -0
- chrys/app/tui/widgets/chat/renderers/read_file.py +399 -0
- chrys/app/tui/widgets/chat/renderers/search.py +348 -0
- chrys/app/tui/widgets/chat/renderers/skill.py +564 -0
- chrys/app/tui/widgets/chat/renderers/sleep.py +287 -0
- chrys/app/tui/widgets/chat/renderers/sub_agent.py +1385 -0
- chrys/app/tui/widgets/chat/renderers/todo.py +114 -0
- chrys/app/tui/widgets/chat/renderers/web.py +69 -0
- chrys/app/tui/widgets/chat/replay.py +1605 -0
- chrys/app/tui/widgets/chat/replay_mount.py +322 -0
- chrys/app/tui/widgets/chat/scroll_controller.py +579 -0
- chrys/app/tui/widgets/chat/selection_controller.py +435 -0
- chrys/app/tui/widgets/chat/session_json.py +508 -0
- chrys/app/tui/widgets/chat/toc_model.py +156 -0
- chrys/app/tui/widgets/chat/tool_call.py +2478 -0
- chrys/app/tui/widgets/chat/tool_registry.py +753 -0
- chrys/app/tui/widgets/chat/tool_renderers.py +247 -0
- chrys/app/tui/widgets/chat/tool_view_builders.py +260 -0
- chrys/app/tui/widgets/chat/transcript_reader.py +57 -0
- chrys/app/tui/widgets/checkbox.py +24 -0
- chrys/app/tui/widgets/chrome/__init__.py +3 -0
- chrys/app/tui/widgets/chrome/app_header.py +283 -0
- chrys/app/tui/widgets/chrome/commands.py +95 -0
- chrys/app/tui/widgets/chrome/file_index.py +68 -0
- chrys/app/tui/widgets/chrome/file_scanner.py +670 -0
- chrys/app/tui/widgets/chrome/footer.py +284 -0
- chrys/app/tui/widgets/chrome/image_paste.py +351 -0
- chrys/app/tui/widgets/chrome/input_bar.py +1299 -0
- chrys/app/tui/widgets/chrome/status_bar.py +911 -0
- chrys/app/tui/widgets/chrome/suggestion_list.py +652 -0
- chrys/app/tui/widgets/click_affordance.py +24 -0
- chrys/app/tui/widgets/color_picker/__init__.py +254 -0
- chrys/app/tui/widgets/color_picker/controls.py +171 -0
- chrys/app/tui/widgets/color_picker/model.py +89 -0
- chrys/app/tui/widgets/dialog_buttons.py +58 -0
- chrys/app/tui/widgets/dialog_scroll.py +24 -0
- chrys/app/tui/widgets/diff_view/__init__.py +20 -0
- chrys/app/tui/widgets/diff_view/cells.py +59 -0
- chrys/app/tui/widgets/diff_view/code.py +280 -0
- chrys/app/tui/widgets/diff_view/compute.py +286 -0
- chrys/app/tui/widgets/diff_view/gutter.py +161 -0
- chrys/app/tui/widgets/diff_view/inline.py +77 -0
- chrys/app/tui/widgets/diff_view/palette.py +87 -0
- chrys/app/tui/widgets/diff_view/protocols.py +24 -0
- chrys/app/tui/widgets/diff_view/rows.py +138 -0
- chrys/app/tui/widgets/diff_view/unified.py +267 -0
- chrys/app/tui/widgets/diff_view/widget.py +209 -0
- chrys/app/tui/widgets/editor/__init__.py +79 -0
- chrys/app/tui/widgets/editor/coordinates.py +30 -0
- chrys/app/tui/widgets/editor/highlighter.py +139 -0
- chrys/app/tui/widgets/editor/keymaps.py +357 -0
- chrys/app/tui/widgets/editor/types.py +216 -0
- chrys/app/tui/widgets/editor/vim.py +1195 -0
- chrys/app/tui/widgets/editor/widget.py +401 -0
- chrys/app/tui/widgets/file_preview.py +58 -0
- chrys/app/tui/widgets/hatch.py +138 -0
- chrys/app/tui/widgets/input.py +66 -0
- chrys/app/tui/widgets/loading.py +125 -0
- chrys/app/tui/widgets/markdown/__init__.py +25 -0
- chrys/app/tui/widgets/markdown/_utils.py +21 -0
- chrys/app/tui/widgets/markdown/anchors.py +71 -0
- chrys/app/tui/widgets/markdown/blocks.py +115 -0
- chrys/app/tui/widgets/markdown/copyable.py +163 -0
- chrys/app/tui/widgets/markdown/diagram/__init__.py +35 -0
- chrys/app/tui/widgets/markdown/diagram/canvas.py +409 -0
- chrys/app/tui/widgets/markdown/diagram/charts.py +52 -0
- chrys/app/tui/widgets/markdown/diagram/diagnostics.py +101 -0
- chrys/app/tui/widgets/markdown/diagram/geometry.py +46 -0
- chrys/app/tui/widgets/markdown/diagram/layout.py +494 -0
- chrys/app/tui/widgets/markdown/diagram/messages.py +18 -0
- chrys/app/tui/widgets/markdown/diagram/model.py +275 -0
- chrys/app/tui/widgets/markdown/diagram/parser.py +173 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/charts.py +349 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/common.py +403 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/graphs.py +1072 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/planning.py +317 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/schedule.py +298 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/schemas.py +241 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/structure.py +221 -0
- chrys/app/tui/widgets/markdown/diagram/presentation.py +32 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/charts.py +275 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/common.py +73 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/planning.py +157 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/schedule.py +202 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/structure.py +77 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/xy.py +385 -0
- chrys/app/tui/widgets/markdown/diagram/router.py +651 -0
- chrys/app/tui/widgets/markdown/diagram/specs/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/specs/charts.py +94 -0
- chrys/app/tui/widgets/markdown/diagram/specs/planning.py +68 -0
- chrys/app/tui/widgets/markdown/diagram/specs/schedule.py +65 -0
- chrys/app/tui/widgets/markdown/diagram/specs/structure.py +23 -0
- chrys/app/tui/widgets/markdown/diagram/viewer.py +92 -0
- chrys/app/tui/widgets/markdown/links.py +47 -0
- chrys/app/tui/widgets/markdown/math/__init__.py +36 -0
- chrys/app/tui/widgets/markdown/math/layout.py +739 -0
- chrys/app/tui/widgets/markdown/math/markdown.py +291 -0
- chrys/app/tui/widgets/markdown/math/parser.py +325 -0
- chrys/app/tui/widgets/markdown/math/symbols.py +325 -0
- chrys/app/tui/widgets/markdown/navigator.py +80 -0
- chrys/app/tui/widgets/markdown/parser.py +1057 -0
- chrys/app/tui/widgets/markdown/stream.py +73 -0
- chrys/app/tui/widgets/markdown/toc.py +98 -0
- chrys/app/tui/widgets/markdown/viewer.py +146 -0
- chrys/app/tui/widgets/markdown/widget.py +2046 -0
- chrys/app/tui/widgets/marquee.py +101 -0
- chrys/app/tui/widgets/option_menu.py +82 -0
- chrys/app/tui/widgets/page_navigator.py +118 -0
- chrys/app/tui/widgets/select.py +196 -0
- chrys/app/tui/widgets/selection.py +59 -0
- chrys/app/tui/widgets/sidebar/__init__.py +3 -0
- chrys/app/tui/widgets/sidebar/buddy.py +367 -0
- chrys/app/tui/widgets/sidebar/context.py +482 -0
- chrys/app/tui/widgets/sidebar/debug.py +195 -0
- chrys/app/tui/widgets/sidebar/empty_state.py +11 -0
- chrys/app/tui/widgets/sidebar/panel.py +198 -0
- chrys/app/tui/widgets/sidebar/tasks.py +198 -0
- chrys/app/tui/widgets/sidebar/toc.py +166 -0
- chrys/app/tui/widgets/sidebar/toc_types.py +30 -0
- chrys/app/tui/widgets/syntax_theme.py +85 -0
- chrys/app/tui/widgets/text_area.py +258 -0
- chrys/app/tui/widgets/trajectory/__init__.py +8 -0
- chrys/app/tui/widgets/trajectory/chartkit.py +264 -0
- chrys/app/tui/widgets/trajectory/insights.py +1266 -0
- chrys/app/tui/widgets/trajectory/overview.py +1101 -0
- chrys/app/tui/widgets/trajectory/panel.py +983 -0
- chrys/app/tui/widgets/trajectory/presentation.py +311 -0
- chrys/app/tui/widgets/trajectory/session_info.py +124 -0
- chrys/app/tui/widgets/trajectory/text_view.py +124 -0
- chrys/app/tui/widgets/trajectory/timeline.py +380 -0
- chrys/app/tui/widgets/welcome.py +126 -0
- chrys/app/tui/widgets/workflow/__init__.py +5 -0
- chrys/app/tui/widgets/workflow/edges.py +89 -0
- chrys/app/tui/widgets/workflow/graph.py +869 -0
- chrys/app/tui/widgets/workflow/info.py +310 -0
- chrys/app/tui/widgets/workflow/node_view.py +27 -0
- chrys/app/tui/widgets/workflow/output.py +225 -0
- chrys/app/tui/widgets/workflow/panel.py +658 -0
- chrys/app/tui/widgets/workflow/projector.py +324 -0
- chrys/app/tui/widgets/workflow/records.py +69 -0
- chrys/app/tui/widgets/workflow/scrollbar.py +82 -0
- chrys/app/tui/widgets/workflow/selection.py +151 -0
- chrys/app/tui/widgets/workflow/source.py +65 -0
- chrys/app/tui/widgets/workflow/text.py +257 -0
- chrys/app/tui/widgets/workflow/values.py +317 -0
- chrys/foundation/__init__.py +0 -0
- chrys/foundation/branding.py +15 -0
- chrys/foundation/config/__init__.py +7 -0
- chrys/foundation/config/coercion.py +374 -0
- chrys/foundation/config/context.py +34 -0
- chrys/foundation/config/env_file.py +141 -0
- chrys/foundation/config/env_layers.py +370 -0
- chrys/foundation/config/migrations.py +521 -0
- chrys/foundation/config/notification_events.py +22 -0
- chrys/foundation/config/process_settings.py +332 -0
- chrys/foundation/config/runtime_pointer.py +133 -0
- chrys/foundation/config/settings.py +1952 -0
- chrys/foundation/config/settings_store.py +996 -0
- chrys/foundation/config/spec.py +396 -0
- chrys/foundation/config/user_settings.py +140 -0
- chrys/foundation/config/warnings.py +262 -0
- chrys/foundation/config/web_values.py +135 -0
- chrys/foundation/config/yaml_store.py +487 -0
- chrys/foundation/errors/__init__.py +68 -0
- chrys/foundation/errors/_legacy.py +117 -0
- chrys/foundation/errors/_text.py +13 -0
- chrys/foundation/errors/_walk.py +85 -0
- chrys/foundation/errors/classify.py +278 -0
- chrys/foundation/errors/display.py +274 -0
- chrys/foundation/errors/formatting.py +283 -0
- chrys/foundation/errors/kinds.py +67 -0
- chrys/foundation/errors/network.py +497 -0
- chrys/foundation/errors/provider.py +445 -0
- chrys/foundation/errors/route.py +85 -0
- chrys/foundation/events/__init__.py +8 -0
- chrys/foundation/events/bus.py +143 -0
- chrys/foundation/events/types.py +1629 -0
- chrys/foundation/events/workflow.py +38 -0
- chrys/foundation/hosted_tools.py +142 -0
- chrys/foundation/i18n/__init__.py +32 -0
- chrys/foundation/i18n/_catalogs/zh-Hans/LC_MESSAGES/chrys.mo +0 -0
- chrys/foundation/i18n/catalogs.py +42 -0
- chrys/foundation/i18n/formatting.py +202 -0
- chrys/foundation/i18n/locale.py +92 -0
- chrys/foundation/i18n/localizer.py +187 -0
- chrys/foundation/i18n/messages.py +268 -0
- chrys/foundation/io/__init__.py +3 -0
- chrys/foundation/io/result_content.py +237 -0
- chrys/foundation/models/__init__.py +3 -0
- chrys/foundation/models/approval_reuse.py +23 -0
- chrys/foundation/models/ask_user.py +468 -0
- chrys/foundation/models/execution.py +18 -0
- chrys/foundation/models/history_markers.py +115 -0
- chrys/foundation/models/invocations.py +63 -0
- chrys/foundation/models/mutation_scope.py +28 -0
- chrys/foundation/models/session_env.py +87 -0
- chrys/foundation/models/session_surface.py +29 -0
- chrys/foundation/models/todos.py +74 -0
- chrys/foundation/models/turns.py +125 -0
- chrys/foundation/models/workflow_session.py +89 -0
- chrys/foundation/models/workspace.py +59 -0
- chrys/foundation/net/__init__.py +3 -0
- chrys/foundation/net/address_class.py +57 -0
- chrys/foundation/net/route_probe.py +94 -0
- chrys/foundation/net/url.py +107 -0
- chrys/foundation/observability/__init__.py +3 -0
- chrys/foundation/observability/exporters.py +174 -0
- chrys/foundation/observability/gate.py +31 -0
- chrys/foundation/observability/setup.py +497 -0
- chrys/foundation/observability/sink.py +228 -0
- chrys/foundation/patches/__init__.py +19 -0
- chrys/foundation/patches/mdurl_cache.py +59 -0
- chrys/foundation/patches/patcher.py +287 -0
- chrys/foundation/patches/staged_members.py +205 -0
- chrys/foundation/patches/textual_block_border.py +105 -0
- chrys/foundation/patches/textual_button_ansi.py +65 -0
- chrys/foundation/patches/textual_button_ansi_old.txt +68 -0
- chrys/foundation/patches/textual_callback_cache.py +248 -0
- chrys/foundation/patches/textual_callback_dispatch.py +109 -0
- chrys/foundation/patches/textual_compositor_cjk.py +566 -0
- chrys/foundation/patches/textual_dispatch_cache.py +150 -0
- chrys/foundation/patches/textual_ime_cursor_anchor.py +92 -0
- chrys/foundation/patches/textual_kitty_keyboard.py +268 -0
- chrys/foundation/patches/textual_lru_acyclic.py +241 -0
- chrys/foundation/patches/textual_message_pump.py +136 -0
- chrys/foundation/patches/textual_node_diet.py +875 -0
- chrys/foundation/patches/textual_one_shot_timer.py +48 -0
- chrys/foundation/patches/textual_option_list.py +90 -0
- chrys/foundation/patches/textual_precompose.py +89 -0
- chrys/foundation/patches/textual_pruned_tabs.py +69 -0
- chrys/foundation/patches/textual_reflow_reuse.py +1753 -0
- chrys/foundation/patches/textual_removed_node_caches.py +86 -0
- chrys/foundation/patches/textual_removed_screen_callbacks.py +55 -0
- chrys/foundation/patches/textual_selection_extract.py +48 -0
- chrys/foundation/patches/textual_strip_cycles.py +119 -0
- chrys/foundation/patches/textual_tab_selection.py +1611 -0
- chrys/foundation/patches/textual_timer_skip.py +70 -0
- chrys/foundation/patches/textual_utf8_decoder.py +106 -0
- chrys/foundation/patches/textual_win_sleep.py +167 -0
- chrys/foundation/patches/watchdog_fsevents.py +70 -0
- chrys/foundation/patches/watchdog_windows.py +52 -0
- chrys/foundation/platform/__init__.py +556 -0
- chrys/foundation/platform/c_api.py +32 -0
- chrys/foundation/platform/child_reap.py +107 -0
- chrys/foundation/platform/command_line.py +95 -0
- chrys/foundation/platform/files.py +1470 -0
- chrys/foundation/platform/output_capture.py +113 -0
- chrys/foundation/platform/paths.py +193 -0
- chrys/foundation/platform/process.py +1468 -0
- chrys/foundation/platform/pty_output.py +19 -0
- chrys/foundation/platform/runtime_paths.py +281 -0
- chrys/foundation/platform/win_console.py +129 -0
- chrys/foundation/reasoning_origin.py +67 -0
- chrys/foundation/recovery.py +16 -0
- chrys/foundation/retry.py +278 -0
- chrys/foundation/text/__init__.py +3 -0
- chrys/foundation/text/encoding.py +603 -0
- chrys/foundation/text/images.py +304 -0
- chrys/foundation/text/lines.py +24 -0
- chrys/foundation/text/mentions.py +90 -0
- chrys/foundation/text/model_json.py +25 -0
- chrys/foundation/text/reading_order.py +20 -0
- chrys/foundation/text/tokenizer.py +25 -0
- chrys/foundation/text/tool_output.py +287 -0
- chrys/foundation/text/yaml_io.py +62 -0
- chrys/foundation/tool_call_context.py +202 -0
- chrys/foundation/tool_execution_stamp.py +184 -0
- chrys/foundation/tool_invocation_order.py +48 -0
- chrys/foundation/tool_kinds.py +102 -0
- chrys/foundation/tool_result_metadata.py +287 -0
- chrys/foundation/trajectory/__init__.py +10 -0
- chrys/foundation/trajectory/context.py +481 -0
- chrys/foundation/trajectory/envelope.py +571 -0
- chrys/foundation/trajectory/event_types.py +294 -0
- chrys/foundation/trajectory/finalizers.py +46 -0
- chrys/foundation/trajectory/fingerprint.py +62 -0
- chrys/foundation/trajectory/ids.py +155 -0
- chrys/foundation/trajectory/keys.py +99 -0
- chrys/foundation/trajectory/lease.py +173 -0
- chrys/foundation/trajectory/metadata.py +72 -0
- chrys/foundation/trajectory/reader.py +271 -0
- chrys/foundation/trajectory/recovery.py +225 -0
- chrys/foundation/trajectory/revisions.py +152 -0
- chrys/foundation/trajectory/segments.py +225 -0
- chrys/foundation/trajectory/tools.py +117 -0
- chrys/foundation/trajectory/usage.py +151 -0
- chrys/foundation/trajectory/writer.py +972 -0
- chrys/foundation/trajectory_timing.py +97 -0
- chrys/foundation/util/__init__.py +3 -0
- chrys/foundation/util/async_tasks.py +29 -0
- chrys/foundation/util/chrys_headers.py +23 -0
- chrys/foundation/util/env_templates.py +104 -0
- chrys/foundation/util/filenames.py +96 -0
- chrys/foundation/util/header_charset.py +138 -0
- chrys/foundation/util/httpx_helpers.py +12 -0
- chrys/foundation/util/lock.py +236 -0
- chrys/foundation/util/once_close.py +46 -0
- chrys/foundation/util/session_ids.py +29 -0
- chrys/foundation/util/shell_tokens.py +88 -0
- chrys/foundation/util/sub_agent_context.py +38 -0
- chrys/foundation/util/time.py +35 -0
- chrys/foundation/util/unicode_scalars.py +36 -0
- chrys/foundation/vendor/__init__.py +43 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-apple-darwin +0 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-pc-windows-msvc.exe +0 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-unknown-linux-gnu +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-apple-darwin +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-pc-windows-msvc.exe +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-unknown-linux-musl +0 -0
- chrys/kernel/__init__.py +256 -0
- chrys/kernel/_content.py +1866 -0
- chrys/kernel/_loop_recorder.py +631 -0
- chrys/kernel/_null_overlay.py +1026 -0
- chrys/kernel/_result_ceiling.py +216 -0
- chrys/kernel/_serialization.py +431 -0
- chrys/kernel/_tool_arg_errors.py +1629 -0
- chrys/kernel/_tool_execution.py +1172 -0
- chrys/kernel/_tool_expansion.py +26 -0
- chrys/kernel/_types.py +2598 -0
- chrys/kernel/agent.py +994 -0
- chrys/kernel/client.py +1084 -0
- chrys/kernel/compaction.py +1315 -0
- chrys/kernel/exceptions.py +211 -0
- chrys/kernel/exchanges.py +683 -0
- chrys/kernel/identity.py +95 -0
- chrys/kernel/images.py +36 -0
- chrys/kernel/instrumentation.py +1618 -0
- chrys/kernel/loop.py +2308 -0
- chrys/kernel/middleware.py +616 -0
- chrys/kernel/sessions.py +604 -0
- chrys/kernel/tools.py +1061 -0
- chrys/kernel/types.py +126 -0
- chrys/orchestration/__init__.py +3 -0
- chrys/orchestration/engine/__init__.py +3 -0
- chrys/orchestration/engine/assembly.py +236 -0
- chrys/orchestration/engine/build/__init__.py +3 -0
- chrys/orchestration/engine/build/builder.py +1061 -0
- chrys/orchestration/engine/build/construction.py +408 -0
- chrys/orchestration/engine/build/loaded.py +139 -0
- chrys/orchestration/engine/engine.py +789 -0
- chrys/orchestration/engine/execution.py +771 -0
- chrys/orchestration/engine/loader.py +865 -0
- chrys/orchestration/engine/rollback.py +918 -0
- chrys/orchestration/engine/run/__init__.py +3 -0
- chrys/orchestration/engine/run/active_injection.py +419 -0
- chrys/orchestration/engine/run/attachments.py +519 -0
- chrys/orchestration/engine/run/bindings.py +1095 -0
- chrys/orchestration/engine/run/coordinator.py +1062 -0
- chrys/orchestration/engine/run/finalizer.py +417 -0
- chrys/orchestration/engine/run/input_refs.py +60 -0
- chrys/orchestration/engine/run/prompt_content.py +457 -0
- chrys/orchestration/engine/run/resume.py +299 -0
- chrys/orchestration/engine/run/retry.py +948 -0
- chrys/orchestration/engine/run/runner.py +625 -0
- chrys/orchestration/engine/run/runtime_skills.py +146 -0
- chrys/orchestration/engine/run/sub_agent_coordination.py +126 -0
- chrys/orchestration/engine/run/turn_hooks.py +258 -0
- chrys/orchestration/engine/run/turn_state.py +149 -0
- chrys/orchestration/engine/run/working_dir.py +50 -0
- chrys/orchestration/engine/session_lifecycle.py +2013 -0
- chrys/orchestration/engine/state/__init__.py +3 -0
- chrys/orchestration/engine/state/active_session.py +183 -0
- chrys/orchestration/engine/state/controls.py +639 -0
- chrys/orchestration/engine/state/current_agent.py +26 -0
- chrys/orchestration/engine/state/lifecycle_permits.py +395 -0
- chrys/orchestration/engine/state/machine.py +193 -0
- chrys/orchestration/engine/state/session_writer.py +434 -0
- chrys/orchestration/engine/trajectory.py +714 -0
- chrys/orchestration/engine/usage.py +118 -0
- chrys/orchestration/invoker/__init__.py +3 -0
- chrys/orchestration/invoker/acp.py +727 -0
- chrys/orchestration/invoker/acp_protocol.py +1115 -0
- chrys/orchestration/invoker/acp_spec.py +79 -0
- chrys/orchestration/invoker/attempts.py +994 -0
- chrys/orchestration/invoker/child_compaction.py +131 -0
- chrys/orchestration/invoker/child_history.py +99 -0
- chrys/orchestration/invoker/contracts.py +253 -0
- chrys/orchestration/invoker/evidence.py +94 -0
- chrys/orchestration/invoker/kernel.py +343 -0
- chrys/orchestration/invoker/origin.py +79 -0
- chrys/orchestration/invoker/resources.py +343 -0
- chrys/orchestration/invoker/runtime.py +299 -0
- chrys/orchestration/invoker/usage.py +80 -0
- chrys/orchestration/session_hooks.py +117 -0
- chrys/orchestration/session_host.py +1023 -0
- chrys/orchestration/session_resources.py +95 -0
- chrys/orchestration/session_usage.py +219 -0
- chrys/orchestration/startup.py +255 -0
- chrys/orchestration/sub_agents/__init__.py +3 -0
- chrys/orchestration/sub_agents/acp_policy.py +269 -0
- chrys/orchestration/sub_agents/kernel_policy.py +848 -0
- chrys/orchestration/sub_agents/registration.py +44 -0
- chrys/orchestration/sub_agents/shell.py +337 -0
- chrys/orchestration/sub_agents/tools.py +2228 -0
- chrys/orchestration/workflows/__init__.py +3 -0
- chrys/orchestration/workflows/agent_archive.py +156 -0
- chrys/orchestration/workflows/agent_node.py +894 -0
- chrys/orchestration/workflows/agent_node_build.py +687 -0
- chrys/orchestration/workflows/catalog.py +332 -0
- chrys/orchestration/workflows/coordinator.py +771 -0
- chrys/orchestration/workflows/hooks.py +176 -0
- chrys/orchestration/workflows/preview.py +322 -0
- chrys/orchestration/workflows/rollback.py +151 -0
- chrys/orchestration/workflows/runner.py +986 -0
- chrys/orchestration/workflows/session.py +390 -0
- chrys/orchestration/workflows/settings.py +97 -0
- chrys/orchestration/workflows/validation.py +674 -0
- chrys/orchestration/workflows/worker_client.py +1002 -0
- chrys/service/__init__.py +3 -0
- chrys/service/acp_client/__init__.py +65 -0
- chrys/service/acp_client/_transport.py +922 -0
- chrys/service/acp_client/client.py +1311 -0
- chrys/service/acp_client/errors.py +224 -0
- chrys/service/acp_client/protocol.py +220 -0
- chrys/service/acp_client/spawn.py +239 -0
- chrys/service/acp_client/spec.py +100 -0
- chrys/service/agent_middleware/__init__.py +60 -0
- chrys/service/agent_middleware/_metadata_keys.py +52 -0
- chrys/service/agent_middleware/control/__init__.py +3 -0
- chrys/service/agent_middleware/control/approval.py +769 -0
- chrys/service/agent_middleware/control/approval_hooks.py +62 -0
- chrys/service/agent_middleware/control/ask_user.py +207 -0
- chrys/service/agent_middleware/control/interrupt.py +50 -0
- chrys/service/agent_middleware/control/sleep.py +118 -0
- chrys/service/agent_middleware/control/todo.py +117 -0
- chrys/service/agent_middleware/events/__init__.py +3 -0
- chrys/service/agent_middleware/events/hook_dispatch.py +250 -0
- chrys/service/agent_middleware/events/hosted_tools.py +1762 -0
- chrys/service/agent_middleware/events/intermediate_text.py +128 -0
- chrys/service/agent_middleware/events/rejection_metadata.py +50 -0
- chrys/service/agent_middleware/events/result_persistence.py +98 -0
- chrys/service/agent_middleware/events/sub_agent_events.py +951 -0
- chrys/service/agent_middleware/events/tool_error_text.py +22 -0
- chrys/service/agent_middleware/events/tool_events.py +779 -0
- chrys/service/agent_middleware/injection.py +510 -0
- chrys/service/agent_middleware/reminders/__init__.py +91 -0
- chrys/service/agent_middleware/reminders/archive_pointer.py +154 -0
- chrys/service/agent_middleware/reminders/base.py +25 -0
- chrys/service/agent_middleware/reminders/context_usage.py +74 -0
- chrys/service/agent_middleware/reminders/file_change.py +63 -0
- chrys/service/agent_middleware/reminders/mcp.py +39 -0
- chrys/service/agent_middleware/reminders/profile_switch.py +106 -0
- chrys/service/agent_middleware/reminders/runtime_env.py +190 -0
- chrys/service/agent_middleware/reminders/skills.py +44 -0
- chrys/service/agent_middleware/reminders/sub_agents.py +35 -0
- chrys/service/agent_middleware/reminders/todo.py +35 -0
- chrys/service/agent_middleware/reminders/turn_line.py +48 -0
- chrys/service/agent_middleware/response_validation.py +1380 -0
- chrys/service/agent_middleware/system_reminder.py +1107 -0
- chrys/service/agent_middleware/validators.py +396 -0
- chrys/service/analytics/__init__.py +124 -0
- chrys/service/analytics/_actions.py +896 -0
- chrys/service/analytics/_context_evidence.py +357 -0
- chrys/service/analytics/_critical_path.py +179 -0
- chrys/service/analytics/_facts.py +919 -0
- chrys/service/analytics/_insights.py +769 -0
- chrys/service/analytics/_metric_ops.py +83 -0
- chrys/service/analytics/_session_projection.py +460 -0
- chrys/service/analytics/_timeline.py +434 -0
- chrys/service/analytics/_turn_graph.py +1237 -0
- chrys/service/analytics/_turns.py +1377 -0
- chrys/service/analytics/_workflows.py +171 -0
- chrys/service/analytics/aggregation.py +1474 -0
- chrys/service/analytics/classification.py +128 -0
- chrys/service/analytics/export.py +803 -0
- chrys/service/analytics/findings.py +282 -0
- chrys/service/analytics/math.py +79 -0
- chrys/service/analytics/model.py +886 -0
- chrys/service/analytics/reader.py +321 -0
- chrys/service/approval/__init__.py +3 -0
- chrys/service/approval/arbitration.py +102 -0
- chrys/service/approval/command_identity.py +109 -0
- chrys/service/approval/correlation.py +57 -0
- chrys/service/approval/grant_store.py +108 -0
- chrys/service/approval/judge.py +589 -0
- chrys/service/approval/level.py +15 -0
- chrys/service/approval/policy.py +209 -0
- chrys/service/approval/reuse.py +223 -0
- chrys/service/approval/reuse_binding.py +133 -0
- chrys/service/approval/safety_classifier.py +536 -0
- chrys/service/approval/turn_context.py +49 -0
- chrys/service/context/__init__.py +3 -0
- chrys/service/context/compaction/__init__.py +38 -0
- chrys/service/context/compaction/breaker.py +156 -0
- chrys/service/context/compaction/budgets.py +93 -0
- chrys/service/context/compaction/calibration.py +77 -0
- chrys/service/context/compaction/compression.py +690 -0
- chrys/service/context/compaction/current_turn_drop.py +458 -0
- chrys/service/context/compaction/events.py +201 -0
- chrys/service/context/compaction/exclusions.py +261 -0
- chrys/service/context/compaction/groups.py +361 -0
- chrys/service/context/compaction/last_words.py +1996 -0
- chrys/service/context/compaction/last_words_state.py +752 -0
- chrys/service/context/compaction/scoped.py +472 -0
- chrys/service/context/compaction/spill.py +1947 -0
- chrys/service/context/compaction/strategy.py +1116 -0
- chrys/service/context/compaction/summaries.py +319 -0
- chrys/service/context/compaction/turns.py +375 -0
- chrys/service/context/manager.py +174 -0
- chrys/service/context/memory_loader.py +792 -0
- chrys/service/context/middleware/__init__.py +3 -0
- chrys/service/context/middleware/usage.py +332 -0
- chrys/service/context/providers/__init__.py +3 -0
- chrys/service/context/providers/context_management.py +432 -0
- chrys/service/context/providers/history.py +647 -0
- chrys/service/context/providers/memory.py +50 -0
- chrys/service/context/tool_content.py +22 -0
- chrys/service/hooks/__init__.py +37 -0
- chrys/service/hooks/detached_worker.py +118 -0
- chrys/service/hooks/docs/README.md +14 -0
- chrys/service/hooks/docs/authoring.md +684 -0
- chrys/service/hooks/docs/configuration.md +499 -0
- chrys/service/hooks/docs/design.md +359 -0
- chrys/service/hooks/docs/project-hooks.md +348 -0
- chrys/service/hooks/events.py +157 -0
- chrys/service/hooks/loader.py +665 -0
- chrys/service/hooks/manager.py +807 -0
- chrys/service/hooks/matcher.py +111 -0
- chrys/service/hooks/outbox.py +294 -0
- chrys/service/hooks/runner.py +572 -0
- chrys/service/hooks/schema.py +407 -0
- chrys/service/llm/__init__.py +7 -0
- chrys/service/llm/_structured_outputs.py +192 -0
- chrys/service/llm/anthropic_messages/__init__.py +9 -0
- chrys/service/llm/anthropic_messages/client.py +254 -0
- chrys/service/llm/anthropic_messages/decode.py +331 -0
- chrys/service/llm/anthropic_messages/history.py +279 -0
- chrys/service/llm/anthropic_messages/request.py +323 -0
- chrys/service/llm/anthropic_messages/server_tools.py +319 -0
- chrys/service/llm/anthropic_messages/stream.py +297 -0
- chrys/service/llm/anthropic_messages/thinking_binding.py +156 -0
- chrys/service/llm/chat_completions/__init__.py +9 -0
- chrys/service/llm/chat_completions/client.py +369 -0
- chrys/service/llm/chat_completions/decode.py +338 -0
- chrys/service/llm/chat_completions/history.py +657 -0
- chrys/service/llm/chat_completions/reasoning.py +244 -0
- chrys/service/llm/chat_completions/request.py +192 -0
- chrys/service/llm/chat_completions/stream.py +546 -0
- chrys/service/llm/chat_completions/validation.py +308 -0
- chrys/service/llm/clients.py +613 -0
- chrys/service/llm/images.py +65 -0
- chrys/service/llm/mock.py +413 -0
- chrys/service/llm/observer.py +446 -0
- chrys/service/llm/one_shot.py +106 -0
- chrys/service/llm/openai_exceptions.py +93 -0
- chrys/service/llm/openai_responses/__init__.py +10 -0
- chrys/service/llm/openai_responses/client.py +323 -0
- chrys/service/llm/openai_responses/decode.py +432 -0
- chrys/service/llm/openai_responses/history.py +519 -0
- chrys/service/llm/openai_responses/hosted.py +625 -0
- chrys/service/llm/openai_responses/replay.py +629 -0
- chrys/service/llm/openai_responses/request.py +342 -0
- chrys/service/llm/openai_responses/stream.py +847 -0
- chrys/service/llm/openai_timestamps.py +57 -0
- chrys/service/llm/providers.py +107 -0
- chrys/service/llm/proxy_route.py +54 -0
- chrys/service/llm/raw_http_log.py +309 -0
- chrys/service/llm/route_facts.py +63 -0
- chrys/service/llm/route_sessions.py +49 -0
- chrys/service/llm/sdk_anthropic.py +18 -0
- chrys/service/llm/sdk_headers.py +47 -0
- chrys/service/llm/sdk_openai.py +18 -0
- chrys/service/llm/sdk_retry.py +59 -0
- chrys/service/llm/wire_client.py +246 -0
- chrys/service/mcp/__init__.py +3 -0
- chrys/service/mcp/_connection.py +235 -0
- chrys/service/mcp/_http_transport.py +350 -0
- chrys/service/mcp/_progressive.py +459 -0
- chrys/service/mcp/_stdio_transport.py +614 -0
- chrys/service/mcp/_tool_mixins.py +96 -0
- chrys/service/mcp/adapter.py +1031 -0
- chrys/service/mcp/cache.py +476 -0
- chrys/service/mcp/content.py +32 -0
- chrys/service/mcp/errors.py +169 -0
- chrys/service/mcp/owned.py +1330 -0
- chrys/service/mcp/result_limits.py +168 -0
- chrys/service/mcp/thinking_warning.py +95 -0
- chrys/service/mcp/validation.py +74 -0
- chrys/service/mutations/__init__.py +84 -0
- chrys/service/mutations/coordination.py +894 -0
- chrys/service/mutations/detector.py +458 -0
- chrys/service/mutations/git_calibrator.py +397 -0
- chrys/service/mutations/git_state.py +647 -0
- chrys/service/mutations/pipeline.py +779 -0
- chrys/service/mutations/scanner.py +466 -0
- chrys/service/mutations/snapshot_files.py +233 -0
- chrys/service/mutations/store.py +625 -0
- chrys/service/mutations/tool_names.py +15 -0
- chrys/service/mutations/trace.py +246 -0
- chrys/service/mutations/tracker.py +1289 -0
- chrys/service/mutations/types.py +559 -0
- chrys/service/mutations/workspace_changes.py +1583 -0
- chrys/service/profiles/__init__.py +2 -0
- chrys/service/profiles/agents/__init__.py +3 -0
- chrys/service/profiles/agents/builtins/Code.yaml +199 -0
- chrys/service/profiles/agents/builtins/Explore.yaml +62 -0
- chrys/service/profiles/agents/builtins/General.yaml +114 -0
- chrys/service/profiles/agents/builtins/QA.yaml +128 -0
- chrys/service/profiles/agents/loader.py +808 -0
- chrys/service/profiles/agents/registry.py +727 -0
- chrys/service/profiles/agents/schema.py +371 -0
- chrys/service/profiles/agents/serializer.py +196 -0
- chrys/service/profiles/coercion.py +22 -0
- chrys/service/profiles/models/__init__.py +3 -0
- chrys/service/profiles/models/env_bridge.py +157 -0
- chrys/service/profiles/models/loader.py +282 -0
- chrys/service/profiles/models/options.py +438 -0
- chrys/service/profiles/models/registry.py +104 -0
- chrys/service/profiles/models/resolver.py +286 -0
- chrys/service/profiles/models/schema.py +91 -0
- chrys/service/profiles/models/serializer.py +76 -0
- chrys/service/session/__init__.py +3 -0
- chrys/service/session/checkpoint.py +156 -0
- chrys/service/session/history.py +1616 -0
- chrys/service/session/message_metadata.py +151 -0
- chrys/service/session/persistence.py +435 -0
- chrys/service/session/runtime_metadata.py +210 -0
- chrys/service/session/sub_agent_logs.py +1069 -0
- chrys/service/session/sub_agent_transcript.py +654 -0
- chrys/service/skills/__init__.py +3 -0
- chrys/service/skills/adapter.py +408 -0
- chrys/service/skills/constants.py +8 -0
- chrys/service/skills/loader.py +575 -0
- chrys/service/skills/model.py +130 -0
- chrys/service/skills/provider.py +841 -0
- chrys/service/skills/runner.py +390 -0
- chrys/service/state/__init__.py +3 -0
- chrys/service/state/_fork.py +710 -0
- chrys/service/state/_session_files.py +175 -0
- chrys/service/state/_session_meta.py +552 -0
- chrys/service/state/locks.py +154 -0
- chrys/service/state/serializers.py +156 -0
- chrys/service/state/session_catalog.py +361 -0
- chrys/service/state/session_listing.py +84 -0
- chrys/service/state/session_migration.py +446 -0
- chrys/service/state/session_mru.py +315 -0
- chrys/service/state/store.py +2132 -0
- chrys/service/state/workflow.py +158 -0
- chrys/service/text_blocks.py +47 -0
- chrys/service/todos/__init__.py +3 -0
- chrys/service/todos/reminder.py +31 -0
- chrys/service/todos/tracker.py +58 -0
- chrys/service/tools/__init__.py +3 -0
- chrys/service/tools/approval_targets.py +92 -0
- chrys/service/tools/builtins/__init__.py +3 -0
- chrys/service/tools/builtins/ask_user.py +326 -0
- chrys/service/tools/builtins/doc_converter/__init__.py +494 -0
- chrys/service/tools/builtins/doc_converter/artifacts.py +392 -0
- chrys/service/tools/builtins/doc_converter/parsers/__init__.py +3 -0
- chrys/service/tools/builtins/doc_converter/parsers/_table.py +34 -0
- chrys/service/tools/builtins/doc_converter/parsers/base.py +101 -0
- chrys/service/tools/builtins/doc_converter/parsers/docx.py +88 -0
- chrys/service/tools/builtins/doc_converter/parsers/pdf.py +144 -0
- chrys/service/tools/builtins/doc_converter/parsers/pptx.py +103 -0
- chrys/service/tools/builtins/doc_converter/parsers/xls.py +32 -0
- chrys/service/tools/builtins/doc_converter/parsers/xlsx.py +51 -0
- chrys/service/tools/builtins/doc_converter/registry.py +55 -0
- chrys/service/tools/builtins/filesystem.py +1071 -0
- chrys/service/tools/builtins/search.py +1265 -0
- chrys/service/tools/builtins/shell.py +967 -0
- chrys/service/tools/builtins/shell_command_filtering/__init__.py +5 -0
- chrys/service/tools/builtins/shell_command_filtering/policy.py +60 -0
- chrys/service/tools/builtins/shell_command_filtering/presets.py +152 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/__init__.py +358 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/commands.py +416 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/git.py +408 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/syntax.py +87 -0
- chrys/service/tools/builtins/shell_command_filtering/tokenizer.py +214 -0
- chrys/service/tools/builtins/shell_filter.py +24 -0
- chrys/service/tools/builtins/sleep.py +91 -0
- chrys/service/tools/builtins/todo.py +64 -0
- chrys/service/tools/builtins/web/__init__.py +6 -0
- chrys/service/tools/builtins/web/build.py +268 -0
- chrys/service/tools/builtins/web/config.py +338 -0
- chrys/service/tools/builtins/web/credentials.py +37 -0
- chrys/service/tools/builtins/web/fetch/__init__.py +3 -0
- chrys/service/tools/builtins/web/fetch/cache.py +68 -0
- chrys/service/tools/builtins/web/fetch/convert.py +56 -0
- chrys/service/tools/builtins/web/fetch/tool.py +223 -0
- chrys/service/tools/builtins/web/hosted.py +61 -0
- chrys/service/tools/builtins/web/http.py +330 -0
- chrys/service/tools/builtins/web/progress.py +18 -0
- chrys/service/tools/builtins/web/search/__init__.py +3 -0
- chrys/service/tools/builtins/web/search/exa_mcp.py +146 -0
- chrys/service/tools/builtins/web/search/html.py +152 -0
- chrys/service/tools/builtins/web/search/json_api.py +227 -0
- chrys/service/tools/builtins/web/search/providers.py +77 -0
- chrys/service/tools/builtins/web/search/tool.py +218 -0
- chrys/service/tools/builtins/web/search/types.py +28 -0
- chrys/service/tools/builtins/web/tls_identity.py +66 -0
- chrys/service/tools/kinds.py +115 -0
- chrys/service/tools/names.py +47 -0
- chrys/service/tools/registry.py +242 -0
- chrys/service/tools/result_metadata.py +103 -0
- chrys/service/tools/session_artifacts.py +196 -0
- chrys/service/tools/spill.py +218 -0
- chrys/service/tools/workspace_paths.py +81 -0
- chrys/service/trajectory/__init__.py +11 -0
- chrys/service/trajectory/approvals.py +201 -0
- chrys/service/trajectory/compaction.py +420 -0
- chrys/service/trajectory/fork.py +75 -0
- chrys/service/trajectory/hooks.py +217 -0
- chrys/service/trajectory/items.py +41 -0
- chrys/service/trajectory/preparation.py +374 -0
- chrys/service/trajectory/retries.py +95 -0
- chrys/service/trajectory/revisions.py +129 -0
- chrys/service/trajectory/segmented.py +163 -0
- chrys/service/trajectory/session.py +762 -0
- chrys/service/trajectory/state.py +83 -0
- chrys/service/trajectory/sub_agents.py +169 -0
- chrys/service/trajectory/tombstone.py +262 -0
- chrys/service/trajectory/tools.py +323 -0
- chrys/service/trajectory/validation.py +130 -0
- chrys/service/trajectory/waits.py +161 -0
- chrys/service/trajectory/workflow.py +148 -0
- chrys/service/vision.py +128 -0
- chrys/service/workflows/__init__.py +3 -0
- chrys/service/workflows/admission.py +151 -0
- chrys/service/workflows/artifacts.py +109 -0
- chrys/service/workflows/asks.py +130 -0
- chrys/service/workflows/builtins/__init__.py +12 -0
- chrys/service/workflows/builtins/demo-workflow.manifest.json +635 -0
- chrys/service/workflows/builtins/demo-workflow.py +756 -0
- chrys/service/workflows/discovery.py +554 -0
- chrys/service/workflows/environment.py +284 -0
- chrys/service/workflows/graph.py +430 -0
- chrys/service/workflows/history.py +82 -0
- chrys/service/workflows/interpreter.py +107 -0
- chrys/service/workflows/journal.py +334 -0
- chrys/service/workflows/layout.py +56 -0
- chrys/service/workflows/ledger.py +149 -0
- chrys/service/workflows/model_selection.py +18 -0
- chrys/service/workflows/orphans.py +209 -0
- chrys/service/workflows/outcomes.py +46 -0
- chrys/service/workflows/protocol.py +210 -0
- chrys/service/workflows/records.py +142 -0
- chrys/service/workflows/scheduler.py +770 -0
- chrys/service/workflows/sdk/__init__.py +51 -0
- chrys/service/workflows/sdk/_ask.py +167 -0
- chrys/service/workflows/sdk/_builder.py +781 -0
- chrys/service/workflows/sdk/_values.py +92 -0
- chrys/service/workflows/sdk_artifact.py +74 -0
- chrys/service/workflows/store.py +494 -0
- chrys/service/workflows/transcript.py +102 -0
- chrys/service/workflows/values.py +73 -0
- chrys/service/workflows/worker_host.py +1820 -0
- chrys/workflows.py +38 -0
- icode_tui-0.29.1.dist-info/METADATA +104 -0
- icode_tui-0.29.1.dist-info/RECORD +1138 -0
- icode_tui-0.29.1.dist-info/WHEEL +4 -0
- icode_tui-0.29.1.dist-info/entry_points.txt +2 -0
- icode_tui-0.29.1.dist-info/licenses/LICENSE +202 -0
- icode_tui-0.29.1.dist-info/licenses/NOTICE +1955 -0
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
# Demo workflow: a runnable example of every workflow feature, meant to be copied and adapted.
|
|
2
|
+
|
|
3
|
+
# /// script
|
|
4
|
+
# requires-python = ">=3.9"
|
|
5
|
+
# ///
|
|
6
|
+
|
|
7
|
+
r"""Workflow Demo · Project Tour: every workflow feature in one read-only, runnable example.
|
|
8
|
+
|
|
9
|
+
WHAT IT DOES
|
|
10
|
+
It writes a guided tour of the project in the current workspace for someone new to it. Python
|
|
11
|
+
nodes take a bounded inventory of the tree, agent nodes read the code, and you review the draft
|
|
12
|
+
before it becomes the output. Copy this file to `.chrys/workflows/` and edit it to start your own.
|
|
13
|
+
|
|
14
|
+
HOW TO RUN IT
|
|
15
|
+
In the TUI, open Workflows, pick this one and press Start. The input is optional free text naming
|
|
16
|
+
what you want to learn ("how does authentication work?"). The run asks you two things: how deep to
|
|
17
|
+
go, and whether the draft is good.
|
|
18
|
+
|
|
19
|
+
`icode workflow run` is headless: nobody can answer there, so `ctx.ask` fails the node. This demo
|
|
20
|
+
therefore defines its own input lines that settle the questions up front:
|
|
21
|
+
|
|
22
|
+
icode workflow run demo-workflow --input $'interactive: false\ndepth: deep\nhow are errors handled?'
|
|
23
|
+
|
|
24
|
+
interactive: false never ask; the tour is labelled "not reviewed by a human"
|
|
25
|
+
depth: quick | deep skip the depth question (unattended runs default to quick)
|
|
26
|
+
|
|
27
|
+
`$'...'` is bash and zsh quoting. In PowerShell, write each line break as `n inside double quotes:
|
|
28
|
+
|
|
29
|
+
icode workflow run demo-workflow --input "interactive: false`ndepth: deep`nhow are errors handled?"
|
|
30
|
+
|
|
31
|
+
These lines are a convention of this file (see `read_request`), not a workflow feature. The file
|
|
32
|
+
deliberately does not catch the failure of `ctx.ask` to fall back on a default answer: that would
|
|
33
|
+
turn every real fault into "carry on as if the user had agreed".
|
|
34
|
+
|
|
35
|
+
READ-ONLY SCOPE
|
|
36
|
+
Nothing in the project is modified. The Python nodes only list directories and read the head of a
|
|
37
|
+
README; they never import or run project code, run tests, install anything or write a file. The
|
|
38
|
+
walk skips VCS, dependency, cache and virtual-environment directories, never enters a symlinked
|
|
39
|
+
directory, and is bounded in depth and entry count. iCode itself still records the run (session,
|
|
40
|
+
transcripts, outputs) as it does for every workflow.
|
|
41
|
+
|
|
42
|
+
Every agent uses the builtin QA profile, which is read-only by its instructions and has no
|
|
43
|
+
file-writing tools, and each node repeats the rule in `instructions_suffix`. Be precise about what
|
|
44
|
+
that guarantees: QA still carries the `shell` tool (behind an approval the user cannot switch off),
|
|
45
|
+
so the read-only promise rests on instructions and approval, not on the absence of a tool. A
|
|
46
|
+
workflow that needs a tool-level guarantee has to name a profile without `shell`.
|
|
47
|
+
|
|
48
|
+
THE GRAPH
|
|
49
|
+
read_request -> scan_tree -> read_key_files --[must_ask_depth]--> choose_depth (ask) --+
|
|
50
|
+
| v
|
|
51
|
+
+-----------[depth_is_known]--------------------> plan
|
|
52
|
+
|
|
53
|
+
plan --switch--+-- wants_deep --> fan_out --+--> architecture --+
|
|
54
|
+
| +--> entry_points --+--join--> merge_findings --+
|
|
55
|
+
| +--> conventions ---+ |
|
|
56
|
+
+-- default ----> hand_over ----> quick_scan --------------------------------+
|
|
57
|
+
plan ---------------- side edge: carries the run state past the agents --------------------+
|
|
58
|
+
|
|
|
59
|
+
join(combine=carry_state)
|
|
60
|
+
v
|
|
61
|
+
refine (loop, at most MAX_ROUNDS): open_round --> brief --> write_tour --+
|
|
62
|
+
| +--join(combine=keep_round_state)
|
|
63
|
+
+---------- side edge -----------+ |
|
|
64
|
+
v
|
|
65
|
+
review (ask)
|
|
66
|
+
|
|
|
67
|
+
render_tour (output) <---------------------------------------+
|
|
68
|
+
next_steps (output) <---- tour_text <--------------[wants_deep]---------------+
|
|
69
|
+
|
|
70
|
+
WHERE EACH FEATURE IS USED
|
|
71
|
+
WorkflowBuilder, build(), Workflow ...... bottom of the file; the module-level `workflow` is what iCode loads
|
|
72
|
+
wf.python, sync fn(value) ............... read_request, read_key_files, merge_findings, brief, tour_text,
|
|
73
|
+
render_tour
|
|
74
|
+
wf.python, async fn(value) ............... scan_tree
|
|
75
|
+
wf.python, sync fn(value, ctx) .......... plan, fan_out, hand_over, open_round
|
|
76
|
+
wf.python, async fn(value, ctx) .......... choose_depth, review
|
|
77
|
+
returning a WorkflowValue(text, data) .... read_request, plan, open_round, review
|
|
78
|
+
returning a plain str ................... fan_out, hand_over, merge_findings, brief, tour_text, render_tour
|
|
79
|
+
ctx.emit ................................ plan, fan_out, hand_over, choose_depth, open_round, review
|
|
80
|
+
await ctx.ask(Question), Answer ......... choose_depth (one choice between two Options)
|
|
81
|
+
await ctx.ask(str) ...................... review (free text)
|
|
82
|
+
wf.agent, instructions_suffix ........... all six agents: the three readers, quick_scan, write_tour, next_steps
|
|
83
|
+
timeout= ................................ conventions (agent), choose_depth and review (None)
|
|
84
|
+
Retry on a Python node .................. read_key_files
|
|
85
|
+
Retry on an agent node .................. write_tour
|
|
86
|
+
model= (commented example) .............. architecture
|
|
87
|
+
wf.chain ................................ read_request -> scan_tree -> read_key_files, and inside the loop
|
|
88
|
+
wf.edge, plain .......................... choose_depth -> plan, the fan-out, refine -> render_tour
|
|
89
|
+
wf.edge(when=...) ....................... the bypass around choose_depth; refine -> tour_text
|
|
90
|
+
wf.switch(cases, default) ............... plan -> fan_out | hand_over
|
|
91
|
+
wf.join, default combine ................ the three readers into merge_findings
|
|
92
|
+
wf.join(combine=...), SourceValue ....... carry_state (into the loop), keep_round_state (inside it)
|
|
93
|
+
wf.loop, BuilderScope, NodeHandle ....... refine / refine_round
|
|
94
|
+
until=, max_iterations=, on_exhausted= .. refine
|
|
95
|
+
wf.start, wf.output (more than one) ..... bottom of the file
|
|
96
|
+
script metadata block ................... top of the file (`requires-python`)
|
|
97
|
+
|
|
98
|
+
RULES WORTH REMEMBERING
|
|
99
|
+
* A node body is `fn(value)` or `fn(value, ctx)`, sync or async, and returns a WorkflowValue or a
|
|
100
|
+
str. A sync body runs on a worker thread, so blocking calls are fine in it. An async body runs on
|
|
101
|
+
the worker's event loop, so it must not block (see `scan_tree`), and only an async body can
|
|
102
|
+
`await ctx.ask`.
|
|
103
|
+
* Edge predicates, switch cases, a loop's `until` and a join's `combine` must be plain sync
|
|
104
|
+
functions of one argument. There is no async form of any of them; `build()` rejects one.
|
|
105
|
+
* `value.text` reaches everyone. `value.data` (JSON only) reaches Python nodes and predicates, and
|
|
106
|
+
is lost in two places: an agent reads and writes text only, and the default join concatenates
|
|
107
|
+
text only. State that must outlive either travels on a side edge into a `join(combine=...)`,
|
|
108
|
+
which is what `carry_state` and `keep_round_state` do.
|
|
109
|
+
* Handing `data` to an agent is not an error, but the run raises a notice about it, because it is
|
|
110
|
+
usually a mistake. So the node in front of an agent returns a plain str, the prompt and nothing
|
|
111
|
+
else, and the state leaves on the side edge from the node before that one. This file never
|
|
112
|
+
triggers the notice.
|
|
113
|
+
* A node runs once all its in-edges are settled. Sources that were skipped, or whose condition was
|
|
114
|
+
false, are simply absent. With one source present the value passes through unchanged, data
|
|
115
|
+
included; with none present the node is skipped, and so is everything that depends only on it.
|
|
116
|
+
* A timeout is wall-clock per attempt and includes the time spent waiting for a person, so a node
|
|
117
|
+
that asks needs `timeout=None`. Python nodes default to 300 seconds, agents to no deadline.
|
|
118
|
+
* An agent node retries up to 3 attempts by default, a Python node runs once. A retry runs the
|
|
119
|
+
whole body again, so give `Retry` only to a body that is safe to repeat.
|
|
120
|
+
|
|
121
|
+
ENVIRONMENT
|
|
122
|
+
The block at the top of this file is standard inline script metadata. `requires-python` is checked
|
|
123
|
+
against the interpreter that runs the file. iCode does not install `dependencies`; a workflow that
|
|
124
|
+
needs third-party packages points `[tool.chrys] python` at an interpreter or virtual environment
|
|
125
|
+
of its own, relative to the workflow file. A workspace copy could start like this (indented here
|
|
126
|
+
so that it stays an example):
|
|
127
|
+
|
|
128
|
+
# /// script
|
|
129
|
+
# requires-python = ">=3.11"
|
|
130
|
+
# dependencies = ["pyyaml"]
|
|
131
|
+
#
|
|
132
|
+
# [tool.chrys]
|
|
133
|
+
# python = "../../.venv"
|
|
134
|
+
# ///
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
import asyncio
|
|
138
|
+
import os
|
|
139
|
+
from typing import Any
|
|
140
|
+
|
|
141
|
+
from chrys.workflows import (
|
|
142
|
+
BuilderScope,
|
|
143
|
+
NodeContext,
|
|
144
|
+
NodeHandle,
|
|
145
|
+
Option,
|
|
146
|
+
Question,
|
|
147
|
+
Retry,
|
|
148
|
+
SourceValue,
|
|
149
|
+
Workflow,
|
|
150
|
+
WorkflowBuilder,
|
|
151
|
+
WorkflowValue,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
wf = WorkflowBuilder(
|
|
155
|
+
"Workflow Demo · Project Tour",
|
|
156
|
+
description="A read-only guided tour of this project: quick or deep, reviewed by you for up to 2 rounds",
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# ---------------------------------------------------------------------------------------------------
|
|
160
|
+
# Constants
|
|
161
|
+
# ---------------------------------------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
QUICK = "quick"
|
|
164
|
+
DEEP = "deep"
|
|
165
|
+
MAX_ROUNDS = 2 # The draft is written once and revised at most once.
|
|
166
|
+
|
|
167
|
+
# Node names that code refers to by id: a custom combine finds its sources by `SourceValue.node_id`.
|
|
168
|
+
PLAN_NODE = "plan"
|
|
169
|
+
ROUND_NODE = "open_round"
|
|
170
|
+
WRITER_NODE = "write_tour"
|
|
171
|
+
|
|
172
|
+
# Bounds of the inventory. Depth and entry count bound the walk whatever the file system looks like.
|
|
173
|
+
MAX_DEPTH = 3
|
|
174
|
+
MAX_ENTRIES = 250
|
|
175
|
+
README_BYTES = 6000
|
|
176
|
+
README_LINES = 60
|
|
177
|
+
SKIPPED_DIRECTORIES = frozenset(
|
|
178
|
+
{
|
|
179
|
+
".git", ".hg", ".svn",
|
|
180
|
+
"node_modules", "bower_components", "vendor", "site-packages",
|
|
181
|
+
".venv", "venv", "env",
|
|
182
|
+
"__pycache__", ".tox", ".nox", ".cache", ".mypy_cache", ".pytest_cache", ".ruff_cache", ".gradle",
|
|
183
|
+
".idea", ".vscode",
|
|
184
|
+
"build", "dist", "target",
|
|
185
|
+
}
|
|
186
|
+
) # fmt: skip
|
|
187
|
+
README_NAMES = ("readme.md", "readme.rst", "readme.txt", "readme")
|
|
188
|
+
PROJECT_FILES = (
|
|
189
|
+
"pyproject.toml", "setup.py", "requirements.txt", "package.json", "tsconfig.json", "Cargo.toml", "go.mod",
|
|
190
|
+
"pom.xml", "build.gradle", "CMakeLists.txt", "Makefile", "Dockerfile",
|
|
191
|
+
) # fmt: skip
|
|
192
|
+
|
|
193
|
+
TRUE_WORDS = ("true", "yes", "on", "1")
|
|
194
|
+
FALSE_WORDS = ("false", "no", "off", "0")
|
|
195
|
+
ACCEPT_WORDS = ("ok", "okay", "yes", "y", "accept", "accepted", "approve", "approved", "lgtm", "looks good")
|
|
196
|
+
|
|
197
|
+
DEFAULT_FOCUS = "a general orientation for someone who is new to this project"
|
|
198
|
+
|
|
199
|
+
# The state every Python node and predicate can rely on. It rides on `WorkflowValue.data`, so it has to
|
|
200
|
+
# be plain JSON: dicts with string keys, lists, strings, numbers, booleans and None.
|
|
201
|
+
DEFAULT_STATE = {
|
|
202
|
+
"focus": DEFAULT_FOCUS,
|
|
203
|
+
"interactive": True, # False: an unattended run that must never call ctx.ask
|
|
204
|
+
"depth": None, # QUICK, DEEP, or None while it is still undecided
|
|
205
|
+
"round": 0, # refine rounds started so far
|
|
206
|
+
"reviewed": False, # a person looked at the draft
|
|
207
|
+
"accepted": False, # that person accepted it
|
|
208
|
+
"feedback": "", # what they asked to change, when they did not
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
READ_ONLY_RULES = (
|
|
212
|
+
" This is a read-only tour: only read and search the workspace. Never create, edit, move or delete"
|
|
213
|
+
" anything, never run a command that changes something, and do not run tests, builds or installers."
|
|
214
|
+
" Cite files as path:line."
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
READER_TASK = (
|
|
218
|
+
"Help a newcomer understand the project in the current workspace.\n"
|
|
219
|
+
"What they want to learn: {focus}\n\n"
|
|
220
|
+
"A bounded inventory of the project, to get you started (read further yourself):\n\n{inventory}"
|
|
221
|
+
)
|
|
222
|
+
FAN_OUT_NOTE = (
|
|
223
|
+
"\n\nYou are one of three readers working in parallel. Cover only the dimension named in your"
|
|
224
|
+
" instructions; the other two are covered by someone else."
|
|
225
|
+
)
|
|
226
|
+
WRITE_TASK = (
|
|
227
|
+
"Write a guided tour of this project for a newcomer.\n"
|
|
228
|
+
"What they want to learn: {focus}\n\n"
|
|
229
|
+
"Notes from reading the project:\n\n{findings}"
|
|
230
|
+
)
|
|
231
|
+
REVISE_TASK = (
|
|
232
|
+
"Revise the project tour below. The reader reviewed it and asked for this change:\n\n{feedback}\n\n"
|
|
233
|
+
"Return the complete revised tour, not a list of edits.\n\n--- current tour ---\n{draft}"
|
|
234
|
+
)
|
|
235
|
+
# A question is shown as Markdown in a dialog that stays open until it is answered, so the person
|
|
236
|
+
# cannot look anything up in the run meanwhile: put what they need to decide into the question and
|
|
237
|
+
# its option descriptions.
|
|
238
|
+
DEPTH_QUESTION = Question(
|
|
239
|
+
"How deep should the tour go?",
|
|
240
|
+
options=[
|
|
241
|
+
Option(QUICK, "One reader skims the project (the default)"),
|
|
242
|
+
Option(
|
|
243
|
+
DEEP,
|
|
244
|
+
"Three readers study architecture, entry points and conventions in parallel, and the run adds"
|
|
245
|
+
" a list of next steps",
|
|
246
|
+
),
|
|
247
|
+
],
|
|
248
|
+
)
|
|
249
|
+
REVIEW_QUESTION = (
|
|
250
|
+
"{draft}\n\n---\n\n"
|
|
251
|
+
"**Review, round {round} of {rounds}.** Reply `ok` to accept this tour, or describe what to change."
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
# ---------------------------------------------------------------------------------------------------
|
|
256
|
+
# Helpers (plain functions; only the functions handed to the builder below become nodes)
|
|
257
|
+
# ---------------------------------------------------------------------------------------------------
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _state(value: WorkflowValue) -> dict[str, Any]:
|
|
261
|
+
"""The run state a value carries on its data channel, with defaults for whatever is missing."""
|
|
262
|
+
state: dict[str, Any] = dict(DEFAULT_STATE)
|
|
263
|
+
if isinstance(value.data, dict):
|
|
264
|
+
state.update(value.data)
|
|
265
|
+
return state
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _list_directory(directory: str, depth: int, lines: list[str], counts: dict[str, int]) -> None:
|
|
269
|
+
"""Append an indented listing of *directory* to *lines*; stops at MAX_DEPTH and MAX_ENTRIES."""
|
|
270
|
+
try:
|
|
271
|
+
with os.scandir(directory) as scanner:
|
|
272
|
+
entries = sorted(scanner, key=lambda entry: entry.name)
|
|
273
|
+
except OSError:
|
|
274
|
+
return # An unreadable directory is left out; it is no reason to fail the tour.
|
|
275
|
+
for entry in entries:
|
|
276
|
+
if len(lines) >= MAX_ENTRIES:
|
|
277
|
+
return
|
|
278
|
+
indent = " " * depth
|
|
279
|
+
# follow_symlinks=False: a link to a directory is listed like a file and never entered.
|
|
280
|
+
if entry.is_dir(follow_symlinks=False):
|
|
281
|
+
is_virtualenv = os.path.isfile(os.path.join(entry.path, "pyvenv.cfg"))
|
|
282
|
+
if entry.name in SKIPPED_DIRECTORIES or is_virtualenv:
|
|
283
|
+
continue
|
|
284
|
+
lines.append(f"{indent}{entry.name}/")
|
|
285
|
+
if depth + 1 < MAX_DEPTH:
|
|
286
|
+
_list_directory(entry.path, depth + 1, lines, counts)
|
|
287
|
+
else:
|
|
288
|
+
lines.append(f"{indent}{entry.name}")
|
|
289
|
+
suffix = os.path.splitext(entry.name)[1].lower() or "(no extension)"
|
|
290
|
+
counts[suffix] = counts.get(suffix, 0) + 1
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _inventory(root: str) -> str:
|
|
294
|
+
"""The tree listing and a file-type tally of *root*, as text."""
|
|
295
|
+
lines: list[str] = []
|
|
296
|
+
counts: dict[str, int] = {}
|
|
297
|
+
_list_directory(root, 0, lines, counts)
|
|
298
|
+
most_common = sorted(counts.items(), key=lambda item: (-item[1], item[0]))[:8]
|
|
299
|
+
tally = ", ".join(f"{suffix} {count}" for suffix, count in most_common) or "no files"
|
|
300
|
+
limit = f"; stopped at {MAX_ENTRIES} entries" if len(lines) >= MAX_ENTRIES else ""
|
|
301
|
+
return (
|
|
302
|
+
f"Project: {os.path.basename(root) or root}\n"
|
|
303
|
+
f"File types: {tally}\n\n"
|
|
304
|
+
f"Tree ({MAX_DEPTH} levels; VCS, dependency, cache and virtual-environment directories skipped{limit}):\n"
|
|
305
|
+
+ "\n".join(lines)
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
# ---------------------------------------------------------------------------------------------------
|
|
310
|
+
# Python node bodies, in graph order
|
|
311
|
+
# ---------------------------------------------------------------------------------------------------
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def read_request(value: WorkflowValue) -> WorkflowValue:
|
|
315
|
+
"""Start node. SYNC `fn(value)`, returning a WorkflowValue with `data`.
|
|
316
|
+
|
|
317
|
+
The run's input text arrives as `value.text` (its `data` is None). This body separates the two
|
|
318
|
+
option lines described in the module docstring from the free text, and from here on the options
|
|
319
|
+
travel as structured state on `data` while `text` stays readable prose.
|
|
320
|
+
"""
|
|
321
|
+
lines = value.text.splitlines()
|
|
322
|
+
if len(lines) == 1 and "\\n" in lines[0]:
|
|
323
|
+
# The bash command pasted into PowerShell arrives as one line with literal `\n`s, after the `$` of
|
|
324
|
+
# `$'...'`. Read as written, its options would be ignored without a word.
|
|
325
|
+
first_key = lines[0].partition(":")[0].strip().lstrip("$").strip().lower()
|
|
326
|
+
if first_key in ("interactive", "depth"):
|
|
327
|
+
raise ValueError(
|
|
328
|
+
"The input is one line with a literal \\n in it: $'...' is bash quoting, and PowerShell passes "
|
|
329
|
+
"it on as written. In PowerShell, write each line break as `n inside double quotes: --input "
|
|
330
|
+
'"interactive: false`ndepth: deep`nhow are errors handled?"'
|
|
331
|
+
)
|
|
332
|
+
options: dict[str, str] = {}
|
|
333
|
+
focus_lines: list[str] = []
|
|
334
|
+
for line in lines:
|
|
335
|
+
key, separator, raw = line.partition(":")
|
|
336
|
+
name = key.strip().lower()
|
|
337
|
+
if separator and name in ("interactive", "depth"):
|
|
338
|
+
options[name] = raw.strip().lower()
|
|
339
|
+
else:
|
|
340
|
+
focus_lines.append(line)
|
|
341
|
+
|
|
342
|
+
# A value this demo cannot interpret is an error, not a reason to guess: raising fails the node,
|
|
343
|
+
# and the message is what the user sees.
|
|
344
|
+
switch = options.get("interactive", "true")
|
|
345
|
+
if switch not in TRUE_WORDS + FALSE_WORDS:
|
|
346
|
+
raise ValueError(f"'interactive: {switch}' is not understood; write true or false.")
|
|
347
|
+
depth = options.get("depth") or None
|
|
348
|
+
if depth not in (None, QUICK, DEEP):
|
|
349
|
+
raise ValueError(f"'depth: {depth}' is not understood; write {QUICK} or {DEEP}.")
|
|
350
|
+
|
|
351
|
+
interactive = switch in TRUE_WORDS
|
|
352
|
+
if not interactive and depth is None:
|
|
353
|
+
depth = QUICK # Nobody to ask, nothing requested: the cheaper tour.
|
|
354
|
+
focus = "\n".join(focus_lines).strip() or DEFAULT_FOCUS
|
|
355
|
+
return WorkflowValue(text=focus, data=dict(DEFAULT_STATE, focus=focus, interactive=interactive, depth=depth))
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
async def scan_tree(value: WorkflowValue) -> WorkflowValue:
|
|
359
|
+
"""ASYNC `fn(value)`.
|
|
360
|
+
|
|
361
|
+
An async body runs on the worker's own event loop, next to the machinery that talks to iCode, so
|
|
362
|
+
blocking in it stalls everything else in the run. The directory walk blocks, hence `to_thread`.
|
|
363
|
+
`read_key_files` below does the same kind of file access as a SYNC body and needs no such care:
|
|
364
|
+
the worker already runs sync bodies on a thread. Prefer sync unless the body awaits something.
|
|
365
|
+
|
|
366
|
+
The worker's current directory is the run's workspace, so `os.getcwd()` is the project root.
|
|
367
|
+
"""
|
|
368
|
+
inventory = await asyncio.to_thread(_inventory, os.getcwd())
|
|
369
|
+
# `data` is passed along untouched. Returning only a str here would silently drop it.
|
|
370
|
+
return WorkflowValue(text=inventory, data=value.data)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def read_key_files(value: WorkflowValue) -> WorkflowValue:
|
|
374
|
+
"""SYNC `fn(value)` with a Retry policy (see where the node is declared).
|
|
375
|
+
|
|
376
|
+
Adds the project files found at the top level and the head of the README. A file can change or
|
|
377
|
+
vanish between the listing and the read; the OSError is not caught here, so it fails the attempt
|
|
378
|
+
and the node's Retry runs the body once more. That is only sound because this body is safe to
|
|
379
|
+
repeat: it reads, and nothing else.
|
|
380
|
+
"""
|
|
381
|
+
names = {name.lower(): name for name in os.listdir(".")}
|
|
382
|
+
present = [name for name in PROJECT_FILES if name.lower() in names]
|
|
383
|
+
sections = [value.text, "Project files at the top level: " + (", ".join(present) or "none of the usual ones")]
|
|
384
|
+
for candidate in README_NAMES:
|
|
385
|
+
readme = names.get(candidate)
|
|
386
|
+
if readme is None or not os.path.isfile(readme):
|
|
387
|
+
continue
|
|
388
|
+
with open(readme, "rb") as handle:
|
|
389
|
+
head = handle.read(README_BYTES).decode("utf-8", errors="replace")
|
|
390
|
+
excerpt = "\n".join(head.splitlines()[:README_LINES])
|
|
391
|
+
sections.append(f"{readme} (first {README_LINES} lines at most):\n{excerpt}")
|
|
392
|
+
break
|
|
393
|
+
return WorkflowValue(text="\n\n".join(sections), data=value.data)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def must_ask_depth(value: WorkflowValue) -> bool:
|
|
397
|
+
"""Edge predicate. Predicates are SYNC, take the source's value, and should be cheap and pure."""
|
|
398
|
+
state = _state(value)
|
|
399
|
+
return bool(state["interactive"]) and state["depth"] is None
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def depth_is_known(value: WorkflowValue) -> bool:
|
|
403
|
+
"""The complement of `must_ask_depth`: exactly one of the two edges out of read_key_files fires."""
|
|
404
|
+
return not must_ask_depth(value)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
async def choose_depth(value: WorkflowValue, ctx: NodeContext) -> WorkflowValue:
|
|
408
|
+
"""ASYNC `fn(value, ctx)`: the only shape that can `await ctx.ask`.
|
|
409
|
+
|
|
410
|
+
`ctx.ask` suspends this body until the person driving the run answers. Asked a `Question`, it
|
|
411
|
+
returns an `Answer`: `answer.choice` is the option label they picked (clicking it and typing it
|
|
412
|
+
exactly are the same answer), or None when they typed something else or nothing; `answer.text`
|
|
413
|
+
holds what they typed besides. A list of up to five Questions shows them in one dialog, one tab each
|
|
414
|
+
labelled by its `header`, and returns a tuple of Answers. The node is declared with `timeout=None`
|
|
415
|
+
because a timeout counts the waiting too.
|
|
416
|
+
|
|
417
|
+
In an unattended run this node is never activated: the conditional edges route around it, which
|
|
418
|
+
shows up as "skipped" in the graph. That is one of two ways to keep `ctx.ask` out of a headless
|
|
419
|
+
run; `review` shows the other.
|
|
420
|
+
"""
|
|
421
|
+
answer = await ctx.ask(DEPTH_QUESTION)
|
|
422
|
+
depth = DEEP if answer.choice == DEEP else QUICK # Anything that is not "deep" is the default.
|
|
423
|
+
ctx.emit(f"Depth chosen: {depth}.")
|
|
424
|
+
return WorkflowValue(text=value.text, data=dict(_state(value), depth=depth))
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def plan(value: WorkflowValue, ctx: NodeContext) -> WorkflowValue:
|
|
428
|
+
"""SYNC `fn(value, ctx)`. `ctx.emit` works in every body; only `ctx.ask` needs async.
|
|
429
|
+
|
|
430
|
+
This node has two in-edges, from choose_depth and from the bypass, and exactly one of them carries
|
|
431
|
+
a value in any run. A single present source passes through unchanged, `data` included, so the body
|
|
432
|
+
does not care which way the value came.
|
|
433
|
+
|
|
434
|
+
Its value goes two ways: the switch judges it and hands it to one branch, and a side edge takes
|
|
435
|
+
the same value, state and all, straight to the join in front of the loop. Emitted lines appear
|
|
436
|
+
live under the node and are kept with the run.
|
|
437
|
+
"""
|
|
438
|
+
state = _state(value)
|
|
439
|
+
depth = state["depth"] or QUICK
|
|
440
|
+
readers = "three readers in parallel" if depth == DEEP else "one reader"
|
|
441
|
+
asked = "asked interactively" if state["interactive"] else "unattended run"
|
|
442
|
+
ctx.emit(f"Tour depth: {depth} ({readers}; {asked}).")
|
|
443
|
+
return WorkflowValue(text=value.text, data=dict(state, depth=depth))
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def wants_deep(value: WorkflowValue) -> bool:
|
|
447
|
+
"""Used twice: as the switch case after `plan`, and as the condition on the edge out of the loop.
|
|
448
|
+
|
|
449
|
+
It reads `data`, so it only works where the state has been carried along. Both call sites are
|
|
450
|
+
downstream of Python nodes or a custom combine, never directly downstream of an agent.
|
|
451
|
+
"""
|
|
452
|
+
return _state(value)["depth"] == DEEP
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def _reader_task(value: WorkflowValue) -> str:
|
|
456
|
+
return READER_TASK.format(focus=_state(value)["focus"], inventory=value.text)
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def fan_out(value: WorkflowValue, ctx: NodeContext) -> str:
|
|
460
|
+
"""SYNC `fn(value, ctx)` returning a plain str, which is shorthand for `WorkflowValue(text=...)`.
|
|
461
|
+
|
|
462
|
+
The shorthand carries no `data`, and that is the point: this is the node in front of agents, so
|
|
463
|
+
it returns their prompt and nothing else. An agent's prompt is the `text` of its input. The state
|
|
464
|
+
is not lost, it left `plan` on the side edge.
|
|
465
|
+
|
|
466
|
+
A switch picks ONE target, so fanning out to three agents needs this node in between: the switch
|
|
467
|
+
routes to it, and three plain edges leave it.
|
|
468
|
+
"""
|
|
469
|
+
ctx.emit("Deep tour: three readers start in parallel.")
|
|
470
|
+
return _reader_task(value) + FAN_OUT_NOTE
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def hand_over(value: WorkflowValue, ctx: NodeContext) -> str:
|
|
474
|
+
"""The quick branch of the switch: the same prompt for a single reader, again as a plain str."""
|
|
475
|
+
ctx.emit("Quick tour: one reader skims the project.")
|
|
476
|
+
return _reader_task(value)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def merge_findings(value: WorkflowValue) -> str:
|
|
480
|
+
"""Target of the DEFAULT join over the three readers.
|
|
481
|
+
|
|
482
|
+
Without `combine=`, a join concatenates its present sources in declared order as
|
|
483
|
+
"## <node id>" sections of text, and the result has no `data`. A reader that failed its way to
|
|
484
|
+
"skipped" would simply be missing from the text.
|
|
485
|
+
"""
|
|
486
|
+
return "Notes from three readers, one section each.\n\n" + value.text
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def carry_state(sources: list[SourceValue]) -> WorkflowValue:
|
|
490
|
+
"""CUSTOM combine of the join in front of the loop. SYNC, one argument, at most 30 seconds.
|
|
491
|
+
|
|
492
|
+
`sources` holds only the sources that produced a value, in declared order. Each is a SourceValue:
|
|
493
|
+
`node_id`, `activation_id` and `value`. Here that is always `plan` (the side edge) plus whichever
|
|
494
|
+
branch of the switch ran; the other branch was skipped and is absent.
|
|
495
|
+
|
|
496
|
+
This is the reunion the side edge exists for: the findings are text that came through agents, and
|
|
497
|
+
the state comes from `plan`, where it last existed.
|
|
498
|
+
"""
|
|
499
|
+
by_node = {source.node_id: source.value for source in sources}
|
|
500
|
+
findings = [source.value.text for source in sources if source.node_id != PLAN_NODE]
|
|
501
|
+
return WorkflowValue(text="\n\n".join(findings), data=_state(by_node[PLAN_NODE]))
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def open_round(value: WorkflowValue, ctx: NodeContext) -> WorkflowValue:
|
|
505
|
+
"""Loop-body ENTRY. Round 1 receives the loop's input; every later round the previous EXIT value.
|
|
506
|
+
|
|
507
|
+
So in round 1 `value.text` is the findings, and in round 2 it is the draft that `review` returned,
|
|
508
|
+
with the reviewer's feedback on `data`. The round counter lives on `data` as well: a loop keeps no
|
|
509
|
+
variables between rounds other than the value it hands from exit to entry.
|
|
510
|
+
"""
|
|
511
|
+
state = _state(value)
|
|
512
|
+
round_number = state["round"] + 1
|
|
513
|
+
ctx.emit("Round 1: writing the tour." if round_number == 1 else f"Round {round_number}: revising the tour.")
|
|
514
|
+
return WorkflowValue(text=value.text, data=dict(state, round=round_number))
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def brief(value: WorkflowValue) -> str:
|
|
518
|
+
"""The node in front of the writer: it turns the round's value into a prompt, a plain str.
|
|
519
|
+
|
|
520
|
+
The same split as `plan` and `fan_out` above. `open_round` owns the state and sends it around the
|
|
521
|
+
agent; this node only writes the prompt.
|
|
522
|
+
"""
|
|
523
|
+
state = _state(value)
|
|
524
|
+
if state["round"] == 1:
|
|
525
|
+
return WRITE_TASK.format(focus=state["focus"], findings=value.text)
|
|
526
|
+
return REVISE_TASK.format(feedback=state["feedback"], draft=value.text)
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
def keep_round_state(sources: list[SourceValue]) -> WorkflowValue:
|
|
530
|
+
"""CUSTOM combine inside the loop: the writer's text, with the state `open_round` sent around it."""
|
|
531
|
+
by_node = {source.node_id: source.value for source in sources}
|
|
532
|
+
return WorkflowValue(text=by_node[WRITER_NODE].text, data=_state(by_node[ROUND_NODE]))
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
async def review(value: WorkflowValue, ctx: NodeContext) -> WorkflowValue:
|
|
536
|
+
"""Loop-body EXIT, and the second `ctx.ask`. Its return value is what `until` judges.
|
|
537
|
+
|
|
538
|
+
The other way to keep `ctx.ask` out of an unattended run: branch inside the body. Routing around
|
|
539
|
+
the node, as choose_depth does, is not an option for a loop's exit, which must produce a value in
|
|
540
|
+
every round (a round without one fails the run).
|
|
541
|
+
|
|
542
|
+
An unattended run is recorded as NOT reviewed. It does not pretend that somebody approved.
|
|
543
|
+
"""
|
|
544
|
+
state = _state(value)
|
|
545
|
+
if not state["interactive"]:
|
|
546
|
+
ctx.emit("Unattended run: nobody reviews the draft.")
|
|
547
|
+
return WorkflowValue(text=value.text, data=dict(state, reviewed=False, accepted=False))
|
|
548
|
+
|
|
549
|
+
question = REVIEW_QUESTION.format(draft=value.text, round=state["round"], rounds=MAX_ROUNDS)
|
|
550
|
+
answer = (await ctx.ask(question)).strip()
|
|
551
|
+
accepted = answer.lower().rstrip(".!") in ACCEPT_WORDS
|
|
552
|
+
ctx.emit("Accepted." if accepted else "Changes requested.")
|
|
553
|
+
# Only an explicit word of acceptance counts; an empty answer is not one.
|
|
554
|
+
feedback = "" if accepted else answer or "(no details given)"
|
|
555
|
+
return WorkflowValue(text=value.text, data=dict(state, reviewed=True, accepted=accepted, feedback=feedback))
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def tour_settled(value: WorkflowValue) -> bool:
|
|
559
|
+
"""The loop's `until`: SYNC, judged on the exit value after every round. True ends the loop.
|
|
560
|
+
|
|
561
|
+
An unattended run is settled after one round, because another round has nothing new to go on.
|
|
562
|
+
"""
|
|
563
|
+
state = _state(value)
|
|
564
|
+
return bool(state["accepted"]) or not state["interactive"]
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def tour_text(value: WorkflowValue) -> str:
|
|
568
|
+
"""The node in front of next_steps: the tour as a plain str, the state left behind.
|
|
569
|
+
|
|
570
|
+
The condition on the edge into this node reads `data`, so the loop's value has to carry it; the
|
|
571
|
+
agent after this node must not receive it. One line of Python separates the two.
|
|
572
|
+
"""
|
|
573
|
+
return value.text
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
def render_tour(value: WorkflowValue) -> str:
|
|
577
|
+
"""Output node. Its value appears on the Output tab and is what `icode workflow run` prints.
|
|
578
|
+
|
|
579
|
+
The status line says honestly how the draft got here, including the case where the loop ran out
|
|
580
|
+
of rounds and handed over a draft that nobody accepted.
|
|
581
|
+
"""
|
|
582
|
+
state = _state(value)
|
|
583
|
+
if state["accepted"]:
|
|
584
|
+
status = f"accepted by the reader in round {state['round']}"
|
|
585
|
+
elif not state["reviewed"]:
|
|
586
|
+
status = "NOT reviewed by a human (unattended run)"
|
|
587
|
+
else:
|
|
588
|
+
status = f"DRAFT, not accepted after {state['round']} rounds. Still open: {state['feedback']}"
|
|
589
|
+
return f"# Project tour\n\nFocus: {state['focus']}\nDepth: {state['depth']}\nStatus: {status}\n\n{value.text}"
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
# ---------------------------------------------------------------------------------------------------
|
|
593
|
+
# The refine loop
|
|
594
|
+
# ---------------------------------------------------------------------------------------------------
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
def refine_round(scope: BuilderScope) -> tuple[NodeHandle, NodeHandle]:
|
|
598
|
+
"""Loop body. Called ONCE, at build time, to declare the nodes of a round; it is not run per round.
|
|
599
|
+
|
|
600
|
+
`scope` offers python/agent/edge/switch/join/chain, but no loop (loops do not nest), no start and
|
|
601
|
+
no output. Edges cannot cross the loop boundary: a body node connects only to body nodes, and the
|
|
602
|
+
outside connects to the loop node as a whole. Return the (entry, exit) pair.
|
|
603
|
+
"""
|
|
604
|
+
entry: NodeHandle = scope.python(ROUND_NODE, open_round)
|
|
605
|
+
prompt: NodeHandle = scope.python("brief", brief)
|
|
606
|
+
writer: NodeHandle = scope.agent(
|
|
607
|
+
WRITER_NODE,
|
|
608
|
+
profile="QA",
|
|
609
|
+
# Agents already retry up to 3 attempts, and only failures the backend marks as transient.
|
|
610
|
+
# Retry replaces that default: 2 attempts, and a 5 second pause before the second.
|
|
611
|
+
retry=Retry(max_attempts=2, backoff=5),
|
|
612
|
+
instructions_suffix=(
|
|
613
|
+
"You are writing a guided tour of this project for a newcomer. Keep it under 500 words: what the"
|
|
614
|
+
" project is, how it is laid out, where execution starts, and what to read first. When asked to"
|
|
615
|
+
" revise, return the whole tour again." + READ_ONLY_RULES
|
|
616
|
+
),
|
|
617
|
+
)
|
|
618
|
+
# timeout=None: this node waits for a person, and a timeout would count that wait.
|
|
619
|
+
exit_: NodeHandle = scope.python("review", review, timeout=None)
|
|
620
|
+
|
|
621
|
+
scope.chain(entry, prompt, writer)
|
|
622
|
+
# The side edge again: `open_round` feeds the join directly, so the round's state reaches
|
|
623
|
+
# `review` although the prompt and the writer in between pass text only.
|
|
624
|
+
scope.join([entry, writer], exit_, combine=keep_round_state)
|
|
625
|
+
return entry, exit_
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
# ---------------------------------------------------------------------------------------------------
|
|
629
|
+
# Nodes
|
|
630
|
+
# ---------------------------------------------------------------------------------------------------
|
|
631
|
+
|
|
632
|
+
request: NodeHandle = wf.python("read_request", read_request)
|
|
633
|
+
tree: NodeHandle = wf.python("scan_tree", scan_tree)
|
|
634
|
+
# A Python node runs once unless told otherwise. Retry counts the first execution: 2 attempts means
|
|
635
|
+
# one retry, after an exception or a timeout, 0.5 seconds later.
|
|
636
|
+
key_files: NodeHandle = wf.python("read_key_files", read_key_files, retry=Retry(max_attempts=2, backoff=0.5))
|
|
637
|
+
depth_choice: NodeHandle = wf.python("choose_depth", choose_depth, timeout=None)
|
|
638
|
+
tour_plan: NodeHandle = wf.python(PLAN_NODE, plan)
|
|
639
|
+
|
|
640
|
+
dispatch: NodeHandle = wf.python("fan_out", fan_out)
|
|
641
|
+
architecture: NodeHandle = wf.agent(
|
|
642
|
+
"architecture",
|
|
643
|
+
# `profile` names an agent profile: a builtin one, or one of your own. An unknown name rejects the
|
|
644
|
+
# workflow when it is opened, before anything runs.
|
|
645
|
+
profile="QA",
|
|
646
|
+
# `model` pins this node to one model profile; without it the node uses the run's active model.
|
|
647
|
+
# A name that does not resolve rejects the workflow just like an unknown profile, which is why
|
|
648
|
+
# this shipped file names none. In your own copy:
|
|
649
|
+
# model="my-fast-model",
|
|
650
|
+
# `instructions_suffix` is appended to the profile's own instructions, for this node only.
|
|
651
|
+
instructions_suffix=(
|
|
652
|
+
"Your dimension is architecture: the main components, what each is responsible for, and how they"
|
|
653
|
+
" depend on each other." + READ_ONLY_RULES
|
|
654
|
+
),
|
|
655
|
+
)
|
|
656
|
+
entry_points: NodeHandle = wf.agent(
|
|
657
|
+
"entry_points",
|
|
658
|
+
profile="QA",
|
|
659
|
+
instructions_suffix=(
|
|
660
|
+
"Your dimension is entry points: where execution starts (commands, mains, servers, handlers) and the"
|
|
661
|
+
" path a typical request or command takes through the code." + READ_ONLY_RULES
|
|
662
|
+
),
|
|
663
|
+
)
|
|
664
|
+
conventions: NodeHandle = wf.agent(
|
|
665
|
+
"conventions",
|
|
666
|
+
profile="QA",
|
|
667
|
+
# Agents have no deadline by default. This one gets 10 minutes per attempt.
|
|
668
|
+
timeout=600,
|
|
669
|
+
instructions_suffix=(
|
|
670
|
+
"Your dimension is conventions: how the code is organised and named, how it is tested, and the rules"
|
|
671
|
+
" a contributor is expected to follow." + READ_ONLY_RULES
|
|
672
|
+
),
|
|
673
|
+
)
|
|
674
|
+
merged: NodeHandle = wf.python("merge_findings", merge_findings)
|
|
675
|
+
solo: NodeHandle = wf.python("hand_over", hand_over)
|
|
676
|
+
quick: NodeHandle = wf.agent(
|
|
677
|
+
"quick_scan",
|
|
678
|
+
profile="QA",
|
|
679
|
+
instructions_suffix=(
|
|
680
|
+
"Skim the project and note what a newcomer needs first: its purpose, its layout, where execution"
|
|
681
|
+
" starts, and how it is tested. Be brief." + READ_ONLY_RULES
|
|
682
|
+
),
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
refine: NodeHandle = wf.loop(
|
|
686
|
+
"refine",
|
|
687
|
+
body=refine_round,
|
|
688
|
+
until=tour_settled,
|
|
689
|
+
max_iterations=MAX_ROUNDS,
|
|
690
|
+
# "continue" (the default): when the rounds run out without `until` turning true, the last exit
|
|
691
|
+
# value becomes the loop's output all the same, and render_tour labels it as a draft.
|
|
692
|
+
# The alternative is on_exhausted="fail": the run then ends as failed (loop_exhausted) and no
|
|
693
|
+
# output is produced. Choose it when an unaccepted result must never look like a result.
|
|
694
|
+
on_exhausted="continue",
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
tour: NodeHandle = wf.python("render_tour", render_tour)
|
|
698
|
+
draft_text: NodeHandle = wf.python("tour_text", tour_text)
|
|
699
|
+
steps: NodeHandle = wf.agent(
|
|
700
|
+
"next_steps",
|
|
701
|
+
profile="QA",
|
|
702
|
+
instructions_suffix=(
|
|
703
|
+
"The message is a finished tour of this project. Suggest what the newcomer should do next: up to five"
|
|
704
|
+
" files to read, each with a one-line reason, and up to three small, safe first tasks. Only suggest;"
|
|
705
|
+
" do not carry out any of it." + READ_ONLY_RULES
|
|
706
|
+
),
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
# ---------------------------------------------------------------------------------------------------
|
|
710
|
+
# Edges
|
|
711
|
+
# ---------------------------------------------------------------------------------------------------
|
|
712
|
+
|
|
713
|
+
wf.start(request) # Exactly one start node; it receives the run's input text.
|
|
714
|
+
|
|
715
|
+
# chain(a, b, c) is edge(a, b) plus edge(b, c).
|
|
716
|
+
wf.chain(request, tree, key_files)
|
|
717
|
+
|
|
718
|
+
# Conditional edges. Each `when` is judged on its own against the source's value, so any number of
|
|
719
|
+
# them may fire: none, one or all. These two are complements, which makes them an either/or that
|
|
720
|
+
# routes around the asking node when there is nothing to ask.
|
|
721
|
+
wf.edge(key_files, depth_choice, when=must_ask_depth)
|
|
722
|
+
wf.edge(key_files, tour_plan, when=depth_is_known)
|
|
723
|
+
wf.edge(depth_choice, tour_plan)
|
|
724
|
+
|
|
725
|
+
# A switch is the other either/or: cases are tried in order, the first true one wins, and `default`
|
|
726
|
+
# (required) takes the rest. Exactly one target runs; the others are skipped. One switch per node.
|
|
727
|
+
wf.switch(tour_plan, cases=[(wants_deep, dispatch)], default=solo)
|
|
728
|
+
wf.edge(solo, quick)
|
|
729
|
+
|
|
730
|
+
# Fan-out is nothing special: several plain edges out of one node, and the targets run in parallel.
|
|
731
|
+
wf.edge(dispatch, architecture)
|
|
732
|
+
wf.edge(dispatch, entry_points)
|
|
733
|
+
wf.edge(dispatch, conventions)
|
|
734
|
+
|
|
735
|
+
# Fan-in with the default combine. A join synthesises a node of its own, "join:merge_findings", in
|
|
736
|
+
# front of the target, and the target may have no other in-edge.
|
|
737
|
+
wf.join([architecture, entry_points, conventions], merged)
|
|
738
|
+
|
|
739
|
+
# Fan-in with a custom combine, straight into the loop. `tour_plan` is the side edge that carries
|
|
740
|
+
# the state; of `merged` and `quick` only the branch the switch chose is present.
|
|
741
|
+
wf.join([tour_plan, merged, quick], refine, combine=carry_state)
|
|
742
|
+
|
|
743
|
+
# The loop node stands for its whole body: its value is the last exit value.
|
|
744
|
+
wf.edge(refine, tour)
|
|
745
|
+
wf.edge(refine, draft_text, when=wants_deep) # Fires next to the plain edge above, not instead of it.
|
|
746
|
+
wf.edge(draft_text, steps)
|
|
747
|
+
|
|
748
|
+
# Every output a run produced is reported, in this order. An output that was skipped is left out,
|
|
749
|
+
# so a quick tour has one output and a deep tour two. Outputs must be top-level nodes.
|
|
750
|
+
wf.output(tour)
|
|
751
|
+
wf.output(steps)
|
|
752
|
+
|
|
753
|
+
# iCode loads the module-level name `workflow`. build() validates the whole graph (names, cycles,
|
|
754
|
+
# reachability, callable shapes) and freezes the builder; a mistake surfaces here, when the file is
|
|
755
|
+
# opened, not in the middle of a run.
|
|
756
|
+
workflow: Workflow = wf.build()
|