gflow-cli 0.19.0__tar.gz → 0.20.0__tar.gz

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.
Files changed (432) hide show
  1. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/CHANGELOG.md +11 -1
  2. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/PKG-INFO +1 -1
  3. gflow_cli-0.20.0/docs/AGENT_UI_E2E.md +108 -0
  4. gflow_cli-0.20.0/docs/AGENT_UI_RECON.md +192 -0
  5. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/INDEX.md +2 -1
  6. gflow_cli-0.20.0/docs/LIVE_VERIFICATION_v0.20.0.md +76 -0
  7. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/pyproject.toml +1 -1
  8. gflow_cli-0.20.0/scripts/dev/analyze_agent_ui_capture.py +146 -0
  9. gflow_cli-0.20.0/scripts/e2e/agentic_image_e2e.ps1 +74 -0
  10. gflow_cli-0.20.0/scripts/e2e/capture_agent_toggle.py +101 -0
  11. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/__init__.py +1 -1
  12. gflow_cli-0.20.0/src/gflow_cli/api/transports/drivers/__init__.py +32 -0
  13. gflow_cli-0.20.0/src/gflow_cli/api/transports/drivers/agentic.py +465 -0
  14. gflow_cli-0.20.0/src/gflow_cli/api/transports/drivers/base.py +102 -0
  15. gflow_cli-0.20.0/src/gflow_cli/api/transports/drivers/classic.py +224 -0
  16. gflow_cli-0.20.0/src/gflow_cli/api/transports/drivers/factory.py +135 -0
  17. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/ui_automation.py +131 -16
  18. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/ui_automation_video.py +49 -22
  19. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/errors.py +22 -0
  20. gflow_cli-0.20.0/tests/api/transports/conftest.py +22 -0
  21. gflow_cli-0.20.0/tests/api/transports/drivers/test_agentic.py +500 -0
  22. gflow_cli-0.20.0/tests/api/transports/drivers/test_factory.py +143 -0
  23. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_ui_automation.py +25 -5
  24. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_ui_automation_batch.py +29 -20
  25. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_ui_automation_image_mode.py +55 -16
  26. gflow_cli-0.20.0/tests/features/test_video_agent_ui_steps.py +447 -0
  27. gflow_cli-0.20.0/tests/features/video_agent_ui.feature +34 -0
  28. gflow_cli-0.20.0/tests/scripts/test_analyze_agent_ui_capture.py +113 -0
  29. gflow_cli-0.20.0/tests/smoke/__init__.py +0 -0
  30. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_errors.py +20 -0
  31. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/uv.lock +1 -1
  32. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/README.md +0 -0
  33. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/active.md +0 -0
  34. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/branch-review.md +0 -0
  35. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/changelog.md +0 -0
  36. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/check.md +0 -0
  37. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/doc-review.md +0 -0
  38. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/known-issues.md +0 -0
  39. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/next.md +0 -0
  40. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/plan.md +0 -0
  41. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/pr-council-review.md +0 -0
  42. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/predict.md +0 -0
  43. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/release.md +0 -0
  44. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/scenario.md +0 -0
  45. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.claude/commands/gflow/status.md +0 -0
  46. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.env.template +0 -0
  47. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.gitattributes +0 -0
  48. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.github/CODEOWNERS +0 -0
  49. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  50. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.github/copilot-instructions.md +0 -0
  51. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.github/dependabot.yml +0 -0
  52. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.github/workflows/ci.yml +0 -0
  53. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.github/workflows/external-pr-triage.yml +0 -0
  54. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.github/workflows/governance-advisory.yml +0 -0
  55. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.github/workflows/governance-benchmark.yml +0 -0
  56. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.github/workflows/release.yml +0 -0
  57. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.gitignore +0 -0
  58. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.gitleaks.toml +0 -0
  59. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.pre-commit-config.yaml +0 -0
  60. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/.secrets.baseline +0 -0
  61. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/AGENTS.md +0 -0
  62. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/CLAUDE.md +0 -0
  63. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/CONFIGURATION.md +0 -0
  64. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/CONTRIBUTING.md +0 -0
  65. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/DISCLAIMER.md +0 -0
  66. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/GEMINI.md +0 -0
  67. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/KNOWN_ISSUES.md +0 -0
  68. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/LICENSE +0 -0
  69. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/PLAN.md +0 -0
  70. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/README.md +0 -0
  71. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/RELEASE.md +0 -0
  72. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/ROADMAP.md +0 -0
  73. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/conftest.py +0 -0
  74. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docker-compose.yml +0 -0
  75. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/AGENT_GUIDE.md +0 -0
  76. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/ARCHITECTURE.md +0 -0
  77. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/AUTHENTICATION.md +0 -0
  78. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/CHARACTER.md +0 -0
  79. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/CHARACTER_RECON.md +0 -0
  80. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/CONFIGURATION.md +0 -0
  81. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/DATA_LAYER.md +0 -0
  82. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/DEBUGGING.md +0 -0
  83. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/DEMOS.md +0 -0
  84. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/DEVELOPMENT.md +0 -0
  85. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/E2E_TESTING.md +0 -0
  86. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/EXTERNAL_STORAGE.md +0 -0
  87. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/GITHUB.md +0 -0
  88. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/GOVERNANCE_BENCHMARK.md +0 -0
  89. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/IMAGE_UPSCALE_RECON.md +0 -0
  90. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_data_layer.md +0 -0
  91. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_image_batch.md +0 -0
  92. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.10.0.md +0 -0
  93. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.11.0.md +0 -0
  94. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.12.0.md +0 -0
  95. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.13.0.md +0 -0
  96. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.16.0.md +0 -0
  97. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.17.0.md +0 -0
  98. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.18.0.md +0 -0
  99. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.19.0.md +0 -0
  100. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.7.0.md +0 -0
  101. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.8.1.md +0 -0
  102. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.9.0.md +0 -0
  103. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.9.1.md +0 -0
  104. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_video_download.md +0 -0
  105. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/MOVIE.md +0 -0
  106. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/PROJECT_STATUS.md +0 -0
  107. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/SECURITY.md +0 -0
  108. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/USAGE.md +0 -0
  109. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/USER_GUIDE.md +0 -0
  110. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/assets/demo-split-pf.gif +0 -0
  111. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/assets/example-run.gif +0 -0
  112. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/schemas/movie-handoff.schema.json +0 -0
  113. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/docs/superpowers/plans/2026-06-12-issue-174-library-ui-attach/PLAN.md +0 -0
  114. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/examples/README.md +0 -0
  115. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/examples/batch_from_config.py +0 -0
  116. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/examples/multi_prompt_t2i.py +0 -0
  117. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/examples/sample_config.json +0 -0
  118. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/examples/sample_prompts.txt +0 -0
  119. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/examples/single_image_t2i.py +0 -0
  120. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/examples/workflow_chain.py +0 -0
  121. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/llms.txt +0 -0
  122. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/README.md +0 -0
  123. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/01_upload_image.json +0 -0
  124. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/02_batchAsyncGenerateVideoText.json +0 -0
  125. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/03_batchCheckAsyncVideoGenerationStatus.json +0 -0
  126. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/04_archive_workflow.json +0 -0
  127. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/05_createProject.json +0 -0
  128. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/06_batchGenerateImages.json +0 -0
  129. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/07_batchGenerateImages_seeded.json +0 -0
  130. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/08_batchAsyncGenerateVideoStartAndEndImage.json +0 -0
  131. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/09_batchAsyncGenerateVideoReferenceImages.json +0 -0
  132. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/10_batchCheckAsyncVideoGenerationStatus_successful.json +0 -0
  133. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/11_batchCheckAsyncVideoGenerationStatus_failed.json +0 -0
  134. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/12_create_scene.json +0 -0
  135. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/13_sceneWorkflows_update.json +0 -0
  136. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/14_get_scene_workflows.json +0 -0
  137. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/samples/captured/15_commit_flowWorkflow.json +0 -0
  138. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/ci/check_doc_links.py +0 -0
  139. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/ci/check_materiality.py +0 -0
  140. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/ci/check_repo_hygiene.py +0 -0
  141. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/debug_editor.py +0 -0
  142. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/debug_gen_settings.py +0 -0
  143. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/debug_settings.py +0 -0
  144. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/_recording_client.py +0 -0
  145. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/_spike_common.py +0 -0
  146. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/active_plan.py +0 -0
  147. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/capture_i2v_frame_slots_dom.py +0 -0
  148. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/capture_i2v_intercept_submit.py +0 -0
  149. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/capture_i2v_model_select_repro.py +0 -0
  150. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/capture_i2v_post_bind_state.py +0 -0
  151. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/capture_image_add_media_dom.py +0 -0
  152. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/capture_locale_invariants.py +0 -0
  153. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/cdp_drive_and_probe.py +0 -0
  154. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/character_create_spike.py +0 -0
  155. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/character_create_spike_v2.py +0 -0
  156. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/dump_character_selectors.js +0 -0
  157. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/make_project.py +0 -0
  158. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/materiality_backtest.py +0 -0
  159. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/monitor_pr_38.py +0 -0
  160. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/patch_character.py +0 -0
  161. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/record_flow_capture.py +0 -0
  162. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/skillopt/README.md +0 -0
  163. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/skillopt/harness.py +0 -0
  164. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/skillopt/tasks.json +0 -0
  165. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_char_editor_dom.py +0 -0
  166. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_char_gen_capture.py +0 -0
  167. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_image_upscale_capture.py +0 -0
  168. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_image_upscale_drive.py +0 -0
  169. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_image_upscale_recaptcha_action.py +0 -0
  170. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_image_upscale_rest_probe.py +0 -0
  171. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_issue170_picker_locale_recon.py +0 -0
  172. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_issue174_library_ui_recon.py +0 -0
  173. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_movie_attach_payload.py +0 -0
  174. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_movie_entity_recon.py +0 -0
  175. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_movie_gen_capture.py +0 -0
  176. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_movie_picker_select.py +0 -0
  177. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_movie_voice_list.py +0 -0
  178. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_patch_entity.py +0 -0
  179. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/dev/spike_patchright.py +0 -0
  180. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/diag/README.md +0 -0
  181. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/diag/capture_flow_traffic.py +0 -0
  182. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/diag/memory_profile.py +0 -0
  183. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/diag/recaptcha_mint.py +0 -0
  184. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/record_demo.ps1 +0 -0
  185. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/smoke_image.py +0 -0
  186. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/smoke_real_chrome_image.py +0 -0
  187. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/smoke_video_editor.py +0 -0
  188. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/smoke_worker_style.py +0 -0
  189. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/scripts/verify_chrome_auth_viability.py +0 -0
  190. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/skills/README.md +0 -0
  191. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/skills/gflow-cli/SKILL.md +0 -0
  192. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/skills/plan/SKILL.md +0 -0
  193. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/skills/pr-council-review/SKILL.md +0 -0
  194. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/skills/predict/SKILL.md +0 -0
  195. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/skills/scenario/SKILL.md +0 -0
  196. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/skills/status/SKILL.md +0 -0
  197. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/sonar-project.properties +0 -0
  198. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/__main__.py +0 -0
  199. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/_cli_helpers.py +0 -0
  200. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/__init__.py +0 -0
  201. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/_engine.py +0 -0
  202. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/_retry.py +0 -0
  203. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/_sapisidhash.py +0 -0
  204. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/character.py +0 -0
  205. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/client.py +0 -0
  206. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/dto.py +0 -0
  207. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/image.py +0 -0
  208. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/image_upscale.py +0 -0
  209. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/recaptcha.py +0 -0
  210. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/routes.py +0 -0
  211. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/scene.py +0 -0
  212. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/__init__.py +0 -0
  213. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/_common.py +0 -0
  214. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/_fingerprint.py +0 -0
  215. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/base.py +0 -0
  216. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/experimental/__init__.py +0 -0
  217. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/experimental/bearer.py +0 -0
  218. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py +0 -0
  219. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/experimental/sapisidhash.py +0 -0
  220. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/api/video.py +0 -0
  221. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/__init__.py +0 -0
  222. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/base.py +0 -0
  223. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/cookies.py +0 -0
  224. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/factory.py +0 -0
  225. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/internal_chromium.py +0 -0
  226. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/real_chrome.py +0 -0
  227. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/strategies.py +0 -0
  228. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/verification.py +0 -0
  229. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/browser_manager.py +0 -0
  230. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/chain.py +0 -0
  231. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/chain_manifest.py +0 -0
  232. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/cli.py +0 -0
  233. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_character.py +0 -0
  234. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_data.py +0 -0
  235. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_image.py +0 -0
  236. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_models.py +0 -0
  237. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_movie.py +0 -0
  238. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_run.py +0 -0
  239. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_scene.py +0 -0
  240. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_video.py +0 -0
  241. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/composition.py +0 -0
  242. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/config.py +0 -0
  243. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/__init__.py +0 -0
  244. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/chain_repo.py +0 -0
  245. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/0001_initial.sql +0 -0
  246. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/0002_add_cloud_storage.sql +0 -0
  247. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/0003_add_scene_tables.sql +0 -0
  248. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/0004_add_scene_output_path.sql +0 -0
  249. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/0005_add_chain_links.sql +0 -0
  250. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/0006_add_operations_metadata.sql +0 -0
  251. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/__init__.py +0 -0
  252. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/models.py +0 -0
  253. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/queries.py +0 -0
  254. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/recorder.py +0 -0
  255. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/redaction.py +0 -0
  256. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/repository.py +0 -0
  257. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/data/store.py +0 -0
  258. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/exceptions.py +0 -0
  259. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/image_batch.py +0 -0
  260. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/json_output.py +0 -0
  261. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/manifest.py +0 -0
  262. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/media.py +0 -0
  263. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/movie_manifest.py +0 -0
  264. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/observability.py +0 -0
  265. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/paths.py +0 -0
  266. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/profile_store.py +0 -0
  267. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/services/__init__.py +0 -0
  268. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/services/character_create.py +0 -0
  269. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/src/gflow_cli/storage.py +0 -0
  270. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tasks/lessons.md +0 -0
  271. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/test_assets/sample_batch.json +0 -0
  272. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/test_assets/sample_batch.tsv +0 -0
  273. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/test_assets/sample_batch_invalid.tsv +0 -0
  274. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/__init__.py +0 -0
  275. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/__init__.py +0 -0
  276. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/fixtures/character_gen_response.json +0 -0
  277. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/fixtures/patch_entity_response.json +0 -0
  278. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_aisandbox_auth_error.py +0 -0
  279. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_aisandbox_auth_headers.py +0 -0
  280. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_bearer_redaction.py +0 -0
  281. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_character.py +0 -0
  282. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_client.py +0 -0
  283. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_client_character.py +0 -0
  284. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_client_delete_characters.py +0 -0
  285. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_client_generate_character.py +0 -0
  286. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_client_image.py +0 -0
  287. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_client_launch_kwargs.py +0 -0
  288. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_client_patch_entity.py +0 -0
  289. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_client_scene.py +0 -0
  290. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_client_upscale.py +0 -0
  291. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_concurrency.py +0 -0
  292. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_dto.py +0 -0
  293. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_engine.py +0 -0
  294. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_image.py +0 -0
  295. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_image_dto.py +0 -0
  296. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_image_upscale.py +0 -0
  297. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_post_json_aisandbox_auth.py +0 -0
  298. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_recaptcha.py +0 -0
  299. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_retry.py +0 -0
  300. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_routes.py +0 -0
  301. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_routes_character.py +0 -0
  302. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_routes_scene.py +0 -0
  303. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_sapisidhash_helper.py +0 -0
  304. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_scene_models.py +0 -0
  305. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_video.py +0 -0
  306. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/test_video_request.py +0 -0
  307. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/__init__.py +0 -0
  308. {gflow_cli-0.19.0/tests/cli → gflow_cli-0.20.0/tests/api/transports/drivers}/__init__.py +0 -0
  309. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_base.py +0 -0
  310. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_bearer.py +0 -0
  311. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_common.py +0 -0
  312. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_evaluate_fetch.py +0 -0
  313. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_factory.py +0 -0
  314. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_fingerprint.py +0 -0
  315. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_sapisidhash.py +0 -0
  316. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_transport_timeout.py +0 -0
  317. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_ui_automation_video.py +0 -0
  318. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/api/transports/test_ui_character_editor.py +0 -0
  319. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/auth/strategies/test_factory.py +0 -0
  320. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/auth/strategies/test_strategies.py +0 -0
  321. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/auth/test_verification.py +0 -0
  322. {gflow_cli-0.19.0/tests/data → gflow_cli-0.20.0/tests/cli}/__init__.py +0 -0
  323. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_auth_list.py +0 -0
  324. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_character.py +0 -0
  325. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_character_create.py +0 -0
  326. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_data.py +0 -0
  327. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_image.py +0 -0
  328. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_image_seed_removed.py +0 -0
  329. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_image_selector_drift.py +0 -0
  330. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_image_upscale.py +0 -0
  331. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_models.py +0 -0
  332. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_movie.py +0 -0
  333. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_run.py +0 -0
  334. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_scene.py +0 -0
  335. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_video.py +0 -0
  336. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_cli_video_chain.py +0 -0
  337. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_error_handling.py +0 -0
  338. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_helpers.py +0 -0
  339. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_movie_manifest.py +0 -0
  340. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_settings_validation.py +0 -0
  341. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/cli/test_t2i_multi_prompt.py +0 -0
  342. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/composition/test_character.py +0 -0
  343. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/composition/test_compose_prompt.py +0 -0
  344. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/composition/test_handoff.py +0 -0
  345. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/conftest.py +0 -0
  346. {gflow_cli-0.19.0/tests/e2e → gflow_cli-0.20.0/tests/data}/__init__.py +0 -0
  347. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/data/test_chain_repo.py +0 -0
  348. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/data/test_find_incomplete_character.py +0 -0
  349. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/data/test_models.py +0 -0
  350. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/data/test_packaging.py +0 -0
  351. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/data/test_recorder.py +0 -0
  352. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/data/test_recorder_character.py +0 -0
  353. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/data/test_redaction.py +0 -0
  354. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/data/test_repository.py +0 -0
  355. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/data/test_scene_persistence.py +0 -0
  356. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/data/test_settings_and_errors.py +0 -0
  357. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/data/test_store_migrations.py +0 -0
  358. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/dev/test_record_flow_capture.py +0 -0
  359. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/dev/test_recording_client.py +0 -0
  360. {gflow_cli-0.19.0/tests/features → gflow_cli-0.20.0/tests/e2e}/__init__.py +0 -0
  361. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/conftest.py +0 -0
  362. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_aisandbox_auth_live.py +0 -0
  363. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_auth_verification_e2e.py +0 -0
  364. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_chain_e2e.py +0 -0
  365. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_character_create_e2e.py +0 -0
  366. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_data_layer_e2e.py +0 -0
  367. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_i2v_flags_e2e.py +0 -0
  368. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_image_batch_e2e.py +0 -0
  369. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_image_i2i_ref_cap_e2e.py +0 -0
  370. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_json_output_e2e.py +0 -0
  371. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_locale_selectors_e2e.py +0 -0
  372. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_scene_compose_live.py +0 -0
  373. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_transports_e2e.py +0 -0
  374. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_video_r2v_ref_cap_e2e.py +0 -0
  375. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/e2e/test_video_t2v_e2e.py +0 -0
  376. {gflow_cli-0.19.0/tests/fixtures → gflow_cli-0.20.0/tests/features}/__init__.py +0 -0
  377. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/auth.feature +0 -0
  378. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/auth_login.feature +0 -0
  379. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/character_create.feature +0 -0
  380. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/character_read.feature +0 -0
  381. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/conftest.py +0 -0
  382. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/image.feature +0 -0
  383. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/image_upscale.feature +0 -0
  384. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/locale_picker_include.feature +0 -0
  385. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/test_auth_login_steps.py +0 -0
  386. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/test_auth_steps.py +0 -0
  387. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/test_character_create_steps.py +0 -0
  388. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/test_character_read_steps.py +0 -0
  389. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/test_image_steps.py +0 -0
  390. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/test_image_upscale_steps.py +0 -0
  391. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/test_locale_picker_include_steps.py +0 -0
  392. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/test_step_collision_guard.py +0 -0
  393. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/test_video_chain_steps.py +0 -0
  394. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/features/video_chain.feature +0 -0
  395. {gflow_cli-0.19.0/tests/image_batch → gflow_cli-0.20.0/tests/fixtures}/__init__.py +0 -0
  396. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/fixtures/seeded_catalog.py +0 -0
  397. {gflow_cli-0.19.0/tests/integration → gflow_cli-0.20.0/tests/image_batch}/__init__.py +0 -0
  398. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/image_batch/test_image_manifest.py +0 -0
  399. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/image_batch/test_observability_events.py +0 -0
  400. {gflow_cli-0.19.0/tests/services → gflow_cli-0.20.0/tests/integration}/__init__.py +0 -0
  401. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/integration/conftest.py +0 -0
  402. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/integration/constants.py +0 -0
  403. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/integration/test_storage_gcs.py +0 -0
  404. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/integration/test_storage_s3.py +0 -0
  405. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/scripts/test_capture_locale_invariants.py +0 -0
  406. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/scripts/test_check_materiality.py +0 -0
  407. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/scripts/test_check_repo_hygiene.py +0 -0
  408. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/scripts/test_materiality_backtest.py +0 -0
  409. {gflow_cli-0.19.0/tests/smoke → gflow_cli-0.20.0/tests/services}/__init__.py +0 -0
  410. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/services/test_character_create_redaction.py +0 -0
  411. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/services/test_character_create_saga.py +0 -0
  412. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/services/test_character_gen_no_direct_post.py +0 -0
  413. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/smoke/test_profile_account_smoke.py +0 -0
  414. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/smoke/test_real_flow.py +0 -0
  415. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_auth.py +0 -0
  416. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_browser_manager.py +0 -0
  417. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_chain.py +0 -0
  418. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_chain_manifest.py +0 -0
  419. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_cli_data.py +0 -0
  420. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_config.py +0 -0
  421. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_conftest_isolation.py +0 -0
  422. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_data_queries.py +0 -0
  423. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_documentation_gate.py +0 -0
  424. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_errors_403.py +0 -0
  425. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_json_output.py +0 -0
  426. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_manifest.py +0 -0
  427. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_marker_registry.py +0 -0
  428. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_media.py +0 -0
  429. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_observability.py +0 -0
  430. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_paths.py +0 -0
  431. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_profile_store.py +0 -0
  432. {gflow_cli-0.19.0 → gflow_cli-0.20.0}/tests/test_smoke.py +0 -0
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.20.0] — 2026-06-15
11
+
12
+ ### Added
13
+
14
+ - **Forced Agentic UI detection** (exit code 25): dynamically detects if the account's Flow editor page has been placed in Google Flow's new Agentic UI A/B cohort. Raises `FlowAgentUiError` and captures a diagnostic viewport screenshot (`debug_forced_agent_ui.png`) to exit cleanly and prevent CLI hangs.
15
+ - **Pluggable Flow UI driver strategy** (`FlowUiDriver`): the UI-automation transport now probes the editor DOM **per generation** (the cohort flaps per page load) and binds a `ClassicFlowUiDriver` or `AgenticFlowUiDriver`, so the two cohorts' selectors never share code. Classic generation is unchanged.
16
+ - **Agentic-cohort image generation** (validated live): when the editor is served the Agentic UI, `gflow image` drives it by encoding settings (count / aspect) into the conversational prompt directive and scraping generated assets directly from the DOM — **deduplicated by media UUID** — because the Agentic UI routes generation through a background Web Worker that defeats page-level network capture. Content-policy blocks (detected in alert/dialog regions) fail fast with `ContentPolicyError` (exit 5). Agentic **video** still raises `FlowAgentUiError` (exit 25) pending a validated scraping path.
17
+ - **`GFLOW_CLI_FORCE_AGENT_UI`** (testing/diagnostic opt-in): forces the agentic composer by clicking the in-input "Agent" toggle after entering the editor, so the agentic path can be exercised deterministically regardless of the server-assigned A/B cohort (which has no client-readable flag). Unset by default — the cohort is auto-detected per generation. See `docs/AGENT_UI_E2E.md`.
18
+
10
19
  ## [0.19.0] — 2026-06-12
11
20
 
12
21
  ### Added
@@ -1549,7 +1558,8 @@ shell-script template that branches on these codes.
1549
1558
 
1550
1559
  First skeleton. Not functional end-to-end yet.
1551
1560
 
1552
- [Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.19.0...HEAD
1561
+ [Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.20.0...HEAD
1562
+ [0.20.0]: https://github.com/ffroliva/gflow-cli/compare/v0.19.0...v0.20.0
1553
1563
  [0.19.0]: https://github.com/ffroliva/gflow-cli/compare/v0.18.0...v0.19.0
1554
1564
  [0.18.0]: https://github.com/ffroliva/gflow-cli/compare/v0.17.0...v0.18.0
1555
1565
  [0.17.0]: https://github.com/ffroliva/gflow-cli/compare/v0.16.0...v0.17.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gflow-cli
3
- Version: 0.19.0
3
+ Version: 0.20.0
4
4
  Summary: Unofficial CLI for Google Flow — drive Veo image-to-video generations from the terminal.
5
5
  Project-URL: Homepage, https://github.com/ffroliva/gflow-cli
6
6
  Project-URL: Issues, https://github.com/ffroliva/gflow-cli/issues
@@ -0,0 +1,108 @@
1
+ # Agentic UI — End-to-End Testing Runbook
2
+
3
+ > How to **deterministically** drive and validate `gflow`'s **Agentic UI** image
4
+ > path against live Google Flow. Companion to
5
+ > [AGENT_UI_RECON.md](AGENT_UI_RECON.md) (the reverse-engineered wire/DOM spec)
6
+ > and [LIVE_VERIFICATION_v0.20.0.md](LIVE_VERIFICATION_v0.20.0.md) (the shipped
7
+ > evidence).
8
+
9
+ ## The problem, and the way in
10
+
11
+ Google Flow serves the composer via a **server-assigned A/B cohort** that flaps
12
+ per page load and has **no client-readable flag** — so the agentic UI cannot be
13
+ *requested*. A naive "retry until the dice land agentic" e2e is unreliable (we
14
+ observed 7 classic loads in a row).
15
+
16
+ **The deterministic trigger:** the *classic* composer exposes an in-input
17
+ **"Agent" toggle**. Clicking it switches the composer into the **same** agentic
18
+ layout — confirmed live (`scripts/e2e/capture_agent_toggle.py`, 2026-06-14):
19
+
20
+ | Signal | Before toggle | After toggle |
21
+ |---|---|---|
22
+ | `crop_*` media trigger | present | **gone** |
23
+ | `tune` settings gear | absent | **present** |
24
+ | Slate composer | present | present |
25
+
26
+ So `tune` appears and `crop_*` disappears → `detect_ui_mode` then binds the
27
+ agentic driver. An **`expand_content`** button opens the full agent side panel.
28
+
29
+ ## Repeatable procedure (deterministic)
30
+
31
+ Set **`GFLOW_CLI_FORCE_AGENT_UI=1`** and the transport clicks the Agent toggle
32
+ after entering the editor, forcing the agentic path on any load. The harness
33
+ runs that and verifies the whole chain:
34
+
35
+ ```powershell
36
+ # From repo root. Image generation is free — safe to run repeatedly.
37
+ .\scripts\e2e\agentic_image_e2e.ps1 -Profile denon82 -Count 1
38
+ .\scripts\e2e\agentic_image_e2e.ps1 -Profile denon82 -Count 3 # also exercises dedup
39
+ ```
40
+
41
+ It PASSes only when all four hold (else it prints which stage failed):
42
+
43
+ 1. `agent_mode_forced activated=true` — the toggle flipped the composer to agentic;
44
+ 2. `ui_driver.bound mode=agentic` — the agentic driver was bound;
45
+ 3. exit 0;
46
+ 4. exactly `-Count` `.jpg` files written (scrape → dedup-by-UUID → download).
47
+
48
+ Run it directly without the harness, too:
49
+
50
+ ```powershell
51
+ $env:GFLOW_CLI_FORCE_AGENT_UI="1"
52
+ uv run gflow image t2i "a ripe banana on a white plate" --profile denon82 --json
53
+ ```
54
+
55
+ > `GFLOW_CLI_FORCE_AGENT_UI` is a **testing/diagnostic opt-in**, not a user
56
+ > feature: it forces the agentic path even when the server served classic.
57
+ > Leave it unset for normal use (the cohort is then auto-detected per
58
+ > generation).
59
+
60
+ ## Validation result (2026-06-14, profile denon82)
61
+
62
+ Forced-agentic runs validated the three assumptions that mocked tests cannot:
63
+
64
+ - **`--count 1`** → exit 0, one **420 KB JPG**. Scrape found the asset, built the
65
+ full-res redirect URL, the `labs.google` download resolved with session
66
+ cookies, and the synthetic `GeneratedImage` fields (`workflow_id=""`, `seed=0`,
67
+ `dimensions=(0,0)`) flowed through the save path without error.
68
+ - **`--count 3`** → exit 0, **3 distinct JPGs** for 3 distinct media UUIDs —
69
+ confirming dedup: the agentic UI's multiple `<img>` nodes per asset collapse to
70
+ the correct count.
71
+
72
+ Classic remains unaffected: live `gflow image t2i` and `gflow video t2v` both
73
+ succeed when the cohort/loads serve classic.
74
+
75
+ ## Knowledge extracted (this cost real e2e runs — keep it)
76
+
77
+ Three render-timing / detection bugs were found **only** by live e2e (mocked
78
+ tests passed throughout); each is now fixed with a regression test:
79
+
80
+ 1. **Cohort detection raced the render.** `get_ui_driver` probed instantly after
81
+ navigation and missed the agentic `tune` indicator (it appeared ~1.25 s
82
+ later), defaulting to classic → spurious `FlowAgentUiError` (exit 25). Fix:
83
+ `detect_ui_mode` polls (8 s window). Test: `test_detect_agentic_after_delayed_render`.
84
+ 2. **The force-toggle raced the render too.** `_force_agent_mode` probed for the
85
+ Agent toggle ~0.8 s after entering the editor, before the composer rendered →
86
+ `agent_toggle_not_found`, silently staying classic. Fix: `wait_for` the toggle
87
+ (8 s) before clicking.
88
+ 3. **Content-policy detection false-positived on static chrome.** Scanning the
89
+ whole `document.body` for "content policy" matched a benign page's footer/menu
90
+ link → every agentic generation died with a spurious `ContentPolicyError`
91
+ (exit 5) in ~6 s. Fix: scope the scan to `[role=alert]`/`[role=dialog]`/
92
+ `[aria-live=assertive]` regions only (a real block surfaces there; a
93
+ chat-message-only refusal is missed, which is the safe trade — a miss →
94
+ timeout beats a false positive that breaks every generation). Tests:
95
+ `test_await_images_raises_content_policy_on_explicit_text`,
96
+ `test_await_images_ignores_body_chrome_policy_text`.
97
+
98
+ General lesson: **any agentic-composer probe must wait for the render** — instant
99
+ DOM checks race Flow's deferred composer mount.
100
+
101
+ ## Still owed
102
+
103
+ - A **captured positive content-policy refusal** in the agentic cohort, to widen
104
+ detection safely beyond alert/dialog regions (chat-message refusals are
105
+ currently missed → timeout). Trigger a disallowed prompt under
106
+ `GFLOW_CLI_FORCE_AGENT_UI=1` and capture how the refusal surfaces.
107
+ - **Agentic video** — only the image path is validated; video stays on the
108
+ classic→`FlowAgentUiError` path pending a video scraping capture.
@@ -0,0 +1,192 @@
1
+ # Agentic Flow UI — Recon Findings
2
+
3
+ > Reverse-engineered 2026-06-14 via `gflow-agent-browser-spike` (CDP-attached
4
+ > real Chrome, `agent-browser@0.27.0`) against the live Flow editor on two
5
+ > Chrome profiles. Account-specific values (emails, project UUIDs) are redacted.
6
+ > Relates to issues #183 (image `t2i` exit 23) and #174 (library-UI entity drop).
7
+
8
+ ## TL;DR
9
+
10
+ - Flow runs a **server-side A/B cohort** that swaps the project composer between
11
+ the **classic** media UI and a new **agentic** UI (chat-style "Agent").
12
+ - It is **volatile**: the *same* Chrome profile rendered the classic composer at
13
+ one point and the agentic composer later on the *same* project — confirming the
14
+ #174 flapping (denon82 reverting within 12h).
15
+ - **gflow's own dedicated automation profiles DO land in the agentic cohort**
16
+ (not only the user's primary profile), so #183/#174 are reproducible through
17
+ gflow — the feature must handle it.
18
+ - The cohort is **NOT discoverable from client-side state**: localStorage keys and
19
+ JS-visible cookies are byte-identical across both UIs, and the prime-suspect key
20
+ `FLOW_MAIN_PROMPT_BOX_STATE` holds only generation settings, not a mode flag.
21
+ → **Detection must be runtime DOM-based**; there is no pre-navigation cookie/flag
22
+ to read or flip.
23
+ - `navigator.webdriver` is **`false`** under CDP-attached real Chrome.
24
+
25
+ ## How it was captured
26
+
27
+ `gflow-agent-browser-spike` launches real Chrome with `--remote-debugging-port`
28
+ on a chosen profile and drives `agent-browser` over CDP. JS is passed as
29
+ **base64-wrapped `eval`** (`eval(atob('…'))`) — multi-line eval args get mangled
30
+ through `npx`, so base64 is the reliable path (this also applies to
31
+ `capture-agent-ui.ps1`, which must minify/encode its eval expressions). Capturing
32
+ the user's **primary** profile required relaunching it with the debug flag (only
33
+ applied on a fresh Chrome start, so all windows must be closed first), which
34
+ triggered a one-time Google 2-Step Verification.
35
+
36
+ ## The two UIs — DOM signature
37
+
38
+ | Signal | Classic | Agentic |
39
+ |---|---|---|
40
+ | `crop_*` inline aspect/mode trigger | **present** (`crop_9_16` etc.) | **absent** |
41
+ | Agent pill (`/\bAgent\b/` + `div[role='textbox']`) | no | **yes** |
42
+ | Chat ligatures `edit_square`, `thumb_up`, `thumb_down`, `flag` | no | **yes** |
43
+ | Agentic ligatures `article_spark`, `apps_spark_2`, `tune` | no | **yes** |
44
+ | "What do you want to create?" placeholder | no | **yes** |
45
+ | Aspect / model / upscale controls | inline `crop_*` dropdown | moved into **"Agent settings"** (`tune`) |
46
+
47
+ Agentic ligature inventory (captured): `accessibility_new, add, add_2,
48
+ apps_spark_2, arrow_back, arrow_forward, arrow_forward_ios, article_spark, close,
49
+ content_copy, dashboard, delete, edit_square, filter_list, flag, help, image,
50
+ left_panel_close, menu, more_vert, movie, search, settings_2, thumb_down,
51
+ thumb_up, tune, warning` — and **no `crop_*`**.
52
+
53
+ ## Gating mechanism (the cohort key)
54
+
55
+ Captured on agentic profiles (both the user's primary and a gflow dedicated
56
+ profile), the client-visible state is **identical**:
57
+
58
+ - **localStorage keys**: `FLOW_MAIN_PROMPT_BOX_STATE`, `FLOW_QUICK_SEARCH_MODE`,
59
+ `_grecaptcha`, `glue.CookieNotificationBar`, `nextauth.message`.
60
+ - **JS-visible cookies**: `EMAIL`, `_ga`, `_ga_X2GNH8R5NS` (any cohort cookie
61
+ would be `httpOnly`, i.e. invisible to JS — recoverable only from HAR request
62
+ headers, not yet captured).
63
+ - **`FLOW_MAIN_PROMPT_BOX_STATE`** value (agentic):
64
+ `{"aspectRatio":"PORTRAIT","selectedImageModelFamily":"narwhal_display","selectedVideoModelFamily":"abra","imageOrVideoMode":"IMAGE","outputsPerPrompt":1,"selectedVideoDuration":4}`
65
+ — generation settings only, **no cohort/mode flag**.
66
+
67
+ Because (a) the key set is identical across classic and agentic, (b) the one
68
+ plausible key holds only settings, and (c) the same profile flaps between states,
69
+ the cohort is concluded to be **server-assigned per page load**, not a readable
70
+ client signal. Pre-navigation detection (read a cookie before driving) is **not
71
+ viable**; runtime DOM classification is the only reliable detector.
72
+
73
+ ## Why the existing handling fails (#183)
74
+
75
+ `_exit_agent_mode` (`ui_automation_video.py`) assumes the classic composer is
76
+ recoverable and keys on `_media_panel_present` (the `crop_*` trigger). In the
77
+ agentic cohort there is **no `crop_*`** at all (aspect moved into Agent settings),
78
+ so the probe never recovers, the selector cascade exhausts, and the caller raises
79
+ `UiSelectorDriftError` (exit 23). The recovery has nothing to recover *to*.
80
+
81
+ ## Recommendation for the feature plan
82
+
83
+ 1. **Detection = runtime DOM classification.** Agentic if *no `crop_*` mode
84
+ trigger* **and** (*Agent pill present* **or** *chat-panel ligatures present*).
85
+ This is what `classify_composer` / a refreshed `_exit_agent_mode` should key
86
+ on. Run it every generation — the cohort flaps, so no caching.
87
+ 2. **No pre-flight cookie/flag gate** — there is no client-readable cohort signal.
88
+ 3. **Response (decide in the feature plan, evidence now supports either):**
89
+ - **Detect + fail cleanly** with a typed `FlowAgentUiError` (own exit code) +
90
+ screenshot — low risk, immediately diagnosable. *Recommended first step.*
91
+ - **Drive the agentic surface** — type into the chat composer, set aspect/model
92
+ via the Agent-settings (`tune`) panel, handle the confirm-before-generating
93
+ gate. Larger; intermittently needed because the cohort flaps.
94
+
95
+ ## DOM scraping validation (2026-06-14, live capture)
96
+
97
+ > Captured via `scripts/capture-media-scrape.ps1` (CDP-attached real Chrome,
98
+ > profile `default`, agentic cohort confirmed: `crop=false, agentPill=true,
99
+ > chatPanel=true`). One image generation, before/after media-DOM snapshot.
100
+ > Artifact: `media-scrape-agentic-20260614-202739.json`.
101
+
102
+ This resolves the open question blocking `AgenticFlowUiDriver.await_images`: **how do
103
+ generated assets surface in the agentic DOM, and is count-delta scraping viable?**
104
+
105
+ - **Assets render as plain remote `<img>` nodes.** After the generation: `blob=0`,
106
+ `data=0`, `canvas=0`, `background-image=0`. The full-res asset is a normal
107
+ `<img src="https://…">`. Count-delta DOM scraping **is viable** — network
108
+ interception is not (see HAR note below).
109
+ - **The src is a same-origin tRPC redirect carrying a stable media id:**
110
+ ```
111
+ https://labs.google/fx/api/trpc/media.getMediaUrlRedirect?name=<uuid>[&mediaUrlType=MEDIA_URL_TYPE_THUMBNAIL]
112
+ ```
113
+ It 302-redirects to the CDN blob; same-origin (`labs.google`) means the session
114
+ cookies authorize the download. The `name=<uuid>` is the **stable backend media id**
115
+ — scraped assets correlate to media identity (usable for the batch ledger / dedup).
116
+ - **Dedupe by `name=<uuid>`, NOT by `<img>` node count.** One generated asset surfaces
117
+ as **multiple** `<img>` nodes (canvas full-res + filmstrip thumbnail + chat preview,
118
+ each in bare and `&mediaUrlType=MEDIA_URL_TYPE_THUMBNAIL` variants). The single
119
+ capture produced **9 new `<img>` nodes for only 3 distinct assets** (~3× inflation).
120
+ `await_images` must extract the `name` query param and **count distinct UUIDs** — a
121
+ raw `initial_count + expected_count` node check would massively over-count.
122
+ - **Page-level HAR captured 0 entries.** Confirms the Web-Worker delegation: the
123
+ `streamChat` + media requests bypass page-level network capture entirely. **DOM
124
+ scraping is the only viable capture path** in the agentic cohort.
125
+ - **`flag` is NOT a content-policy signal.** The warn-symbol probe matched the `flag`
126
+ ligature 11× on a *successful* generation — `flag`/`thumb_up`/`thumb_down` are the
127
+ normal per-message chat affordances. Fail-fast detection must key on `warning` /
128
+ `error` / `block` or specific dialog/stream text, **never `flag`**. *(A deliberate
129
+ content-policy-refusal capture is still outstanding — no positive block sample yet.)*
130
+
131
+ ## Settings via prompt, not the `tune` popover (agentic acts MCP-like)
132
+
133
+ The agentic composer behaves like a conversational agent: generation parameters that the
134
+ classic UI exposes as discrete controls — **output count** (1 / 4 images), **video
135
+ duration** (4/6/8/10 s), and likely **aspect ratio** and **model** — can be expressed in
136
+ **natural language inside the prompt** (e.g. `Generate 4 images of <prompt> in 16:9`), and
137
+ the agent resolves the selection itself.
138
+
139
+ Implications for `AgenticFlowUiDriver`:
140
+ - **`configure_settings` becomes optional.** Encoding settings as a directive prompt
141
+ avoids driving the fragile `tune` → Radix-popover dropdowns — the most drift-prone
142
+ surface on a volatile A/B UI. Prefer prompt-encoding; treat popover automation as a
143
+ fallback only if prompt-steering proves unreliable.
144
+ - **The agent *interprets* the request → verify, don't trust.** It may produce a
145
+ different count or ignore a duration. This is precisely why scrape-and-dedup-by-UUID
146
+ is load-bearing: request N → poll until N distinct new media UUIDs → if the produced
147
+ count differs, raise a typed mismatch rather than silently returning the wrong set.
148
+ - **Compose carefully** so settings directives aren't read as subject text (e.g. a
149
+ `Generate {n} image(s){aspect}: {prompt}` template).
150
+
151
+ ## Open follow-ups
152
+
153
+ - **Content-policy block sample (outstanding):** capture a deliberate-refusal generation
154
+ in the agentic cohort to learn how a block surfaces (chat message vs. dialog vs.
155
+ symbol) — needed to design `await_images` fail-fast. The `flag` symbol is ruled out.
156
+
157
+ ## The Wire & API Endpoint (Resolved via Issue #183 Reporter)
158
+
159
+ The network request logs from the agentic UI have been resolved. The new UX bypasses the direct media generation endpoints (such as `batchGenerateImages`) and routes all generation requests through Google's conversational flow creation agent.
160
+
161
+ - **Endpoint**: `POST https://aisandbox-pa.googleapis.com/v1/flowCreationAgent:streamChat?alt=sse`
162
+ - **Transport**: Server-Sent Events (SSE) stream, preceded by a CORS OPTIONS preflight.
163
+ - **Request Payload Structure**:
164
+ ```json
165
+ {
166
+ "agentSessionId": "<uuid>",
167
+ "agentClientContext": {
168
+ "projectId": "projects/<projectUuid>",
169
+ "clientSessionId": ";<epochMillis>",
170
+ "recaptchaContext": {
171
+ "token": "<recaptcha_token>",
172
+ "applicationType": "RECAPTCHA_APPLICATION_TYPE_WEB"
173
+ },
174
+ "turnNumber": 2
175
+ },
176
+ "userMessage": {
177
+ "userPrompt": {
178
+ "parts": [
179
+ {
180
+ "text": "a red apple"
181
+ }
182
+ ]
183
+ }
184
+ }
185
+ }
186
+ ```
187
+
188
+ ### Why page-level CDP/Playwright instrumentation recorded 0 entries
189
+ During local capture sessions, page-level request event listeners repeatedly recorded `0` API request entries. This occurred because the agentic UI delegates all `streamChat` network requests to a background **Web/Service Worker** target (`type: worker`). Worker-initiated requests completely bypass page-level network event registration in Playwright/CDP and require worker-level target attachments to capture.
190
+
191
+ ### Impact on issue #174 (referenceEntities)
192
+ In the agentic UI, the prompt box is a conversational Slate.js editor. Because generation is driven by the conversation agent stream rather than direct REST inputs, staging library entities (e.g. characters) behaves differently. For the initial implementation phase, driving or injecting into `flowCreationAgent:streamChat` is out of scope. The recommended direction is **Runtime DOM detection + Fail Cleanly** with `FlowAgentUiError` (exit code 23) to prevent automation lockups while the cohort is active.
@@ -35,6 +35,7 @@ Welcome to the `gflow-cli` documentation. This index is the routing layer: it te
35
35
  | **[docs/CHARACTER.md](CHARACTER.md)** | Characters feature spec & system design: domain model, endpoint/cost matrix, sequence diagrams, JSON payloads (I/O), CLI surface, reuse via `referenceEntities` (#145) | Working on `gflow character`, reusing a character in generations, or understanding Flow's character wire protocol |
36
36
  | **[docs/MOVIE.md](MOVIE.md)** | `gflow movie` — multi-scene character-consistent films: manifest format, run lifecycle (browser stays open through generate→poll→download), entity-attach mechanism (Personagens right-click), resume/handoff, credits | Working on `gflow movie`, debugging scene generation, or understanding the character-entity attach + consistency model |
37
37
  | **[docs/IMAGE_UPSCALE_RECON.md](IMAGE_UPSCALE_RECON.md)** | `gflow image upscale` wire protocol: `upsampleImage` endpoint, full clientContext (projectId/sessionId/tool/userPaygateTier), reCAPTCHA action `IMAGE_GENERATION`, inline base64 response, 4K Ultra-gating, REST-is-dead proof | Working on `gflow image upscale`, debugging the 403/reCAPTCHA path, or understanding Flow's image-upscale wire protocol (#171) |
38
+ | **[docs/AGENT_UI_RECON.md](AGENT_UI_RECON.md)** | Agentic Flow UI cohort: classic-vs-agentic DOM signature, server-side + volatile (flapping) A/B gating (no client-readable flag), why `_exit_agent_mode` fails, runtime-DOM-detection recommendation | Touching `_exit_agent_mode` / composer mode-switch, diagnosing exit 23 selector drift, or understanding Flow's agentic-UI A/B (#183/#174) |
38
39
  | **[tasks/lessons.md](../tasks/lessons.md)** | Running notebook of patterns + reviewer findings, dated and traced to commits | Starting a new phase; debugging "why did the council flag this?" |
39
40
  | **[skills/README.md](../skills/README.md)** | Installable agent skill docs (gflow-cli, predict, pr-council-review, scenario) — cross-tool portable Markdown consumed by Claude Code, Cursor, Codex, Gemini CLI, Aider, etc. | Any agent wanting to use gflow-cli correctly |
40
41
  | **[scripts/dev/skillopt/README.md](../scripts/dev/skillopt/README.md)** | SkillOpt mock harness — rollout→score loop for measuring and improving skill doc accuracy across multiple LLM providers | Measuring a skill edit's impact; comparing Claude vs GPT-4o vs Gemini on gflow tasks |
@@ -111,7 +112,7 @@ Slash commands for Claude Code, stored in `.claude/commands/gflow/`. All prefixe
111
112
  **"A gflow command hangs / fails — where do I start?"** → [DEBUGGING § Quick reference](DEBUGGING.md#quick-reference)
112
113
  **"Flow's UI broke a selector — how do I diagnose it?"** → [DEBUGGING § Inspecting Flow's live UI](DEBUGGING.md#inspecting-flows-live-ui)
113
114
  **"What does each `ui_automation.*` log event mean?"** → [DEBUGGING § Listener & HTTP-layer debugging](DEBUGGING.md#listener--http-layer-debugging)
114
- **"What was actually live-verified for the latest release?"** → latest: [LIVE_VERIFICATION_v0.19.0](LIVE_VERIFICATION_v0.19.0.md) (opt-in patchright engine: credit-free `image t2i` end-to-end under `GFLOW_CLI_BROWSER_ENGINE=patchright`, 5-layer ledger + missing-dep/invalid-enum/default-path checks; 403→200 premise documented as pending a hot profile). v0.14.0 / v0.15.0 / v0.15.1 shipped without a dedicated doc (CI + the v0.13.0 baseline). Prior: [v0.18.0](LIVE_VERIFICATION_v0.18.0.md) · [v0.17.0](LIVE_VERIFICATION_v0.17.0.md) · [v0.16.0](LIVE_VERIFICATION_v0.16.0.md) · [v0.13.0](LIVE_VERIFICATION_v0.13.0.md) · [v0.12.0](LIVE_VERIFICATION_v0.12.0.md) · [v0.11.0](LIVE_VERIFICATION_v0.11.0.md) · [v0.10.0](LIVE_VERIFICATION_v0.10.0.md) · [v0.9.1](LIVE_VERIFICATION_v0.9.1.md) · [v0.9.0](LIVE_VERIFICATION_v0.9.0.md) · [v0.8.1](LIVE_VERIFICATION_v0.8.1.md) · [v0.7.0](LIVE_VERIFICATION_v0.7.0.md)
115
+ **"What was actually live-verified for the latest release?"** → latest: [LIVE_VERIFICATION_v0.20.0](LIVE_VERIFICATION_v0.20.0.md) (Agentic UI image generation on `denon82`: classic `image t2i` + `video t2v` unaffected, and forced-agentic `image t2i` count 1 & count 3 with **live dedup-by-UUID** under `GFLOW_CLI_FORCE_AGENT_UI=1`; 5-layer ledger; natural-cohort agentic load + content-policy refusal recorded as not-verified-this-cycle). v0.14.0 / v0.15.0 / v0.15.1 shipped without a dedicated doc (CI + the v0.13.0 baseline). Prior: [v0.19.0](LIVE_VERIFICATION_v0.19.0.md) · [v0.18.0](LIVE_VERIFICATION_v0.18.0.md) · [v0.17.0](LIVE_VERIFICATION_v0.17.0.md) · [v0.16.0](LIVE_VERIFICATION_v0.16.0.md) · [v0.13.0](LIVE_VERIFICATION_v0.13.0.md) · [v0.12.0](LIVE_VERIFICATION_v0.12.0.md) · [v0.11.0](LIVE_VERIFICATION_v0.11.0.md) · [v0.10.0](LIVE_VERIFICATION_v0.10.0.md) · [v0.9.1](LIVE_VERIFICATION_v0.9.1.md) · [v0.9.0](LIVE_VERIFICATION_v0.9.0.md) · [v0.8.1](LIVE_VERIFICATION_v0.8.1.md) · [v0.7.0](LIVE_VERIFICATION_v0.7.0.md)
115
116
  **"Where is the reverse-engineered wire protocol for a feature?"** → the `*_RECON.md` design docs: [CHARACTER_RECON](CHARACTER_RECON.md) (Flow character entity protocol), [IMAGE_UPSCALE_RECON](IMAGE_UPSCALE_RECON.md) (`/v1/flow/upsampleImage` wire). Naming convention: one `<FEATURE>_RECON.md` per reverse-engineered surface, kept as the durable spec after the feature ships.
116
117
  **"What was live-verified for the data layer (PR #58)?"** → [LIVE_VERIFICATION_data_layer](LIVE_VERIFICATION_data_layer.md) — 1 Imagen + 1 Veo credit on denon82, 6-layer ledger (file + magic + Pillow + DB rows + CLI round-trip + structlog)
117
118
  **"What was live-verified for the video-download feature (#29)?"** → [LIVE_VERIFICATION_video_download](LIVE_VERIFICATION_video_download.md)
@@ -0,0 +1,76 @@
1
+ # Live verification — v0.20.0 (Agentic UI driver)
2
+
3
+ > Hand-run against the real Google Flow editor on profile `denon82`. v0.20.0
4
+ > ships the pluggable `FlowUiDriver` strategy and **Agentic-cohort image
5
+ > generation**. The agentic cohort is server-assigned and cannot be forced from
6
+ > the client, so the deterministic trigger `GFLOW_CLI_FORCE_AGENT_UI=1` (clicks
7
+ > the in-input "Agent" toggle) is used to exercise the agentic path. Image
8
+ > generation is credit-free.
9
+
10
+ ## Environment
11
+
12
+ | | |
13
+ |---|---|
14
+ | Branch | `feature/agentic-ui-driver` → `develop` (PR [#189](https://github.com/ffroliva/gflow-cli/pull/189)) |
15
+ | Version | `0.19.0` working tree (pre-v0.20.0 bump) |
16
+ | Profile | `denon82` (real-browser Chrome) |
17
+ | Profile dir | `C:\Users\ffrol\AppData\Local\ffroliva\gflow-cli\profile_denon82` |
18
+ | Date | 2026-06-14 |
19
+ | Transport | `ui_automation` |
20
+ | Engine | `playwright` |
21
+
22
+ ## How to reproduce
23
+
24
+ ```pwsh
25
+ $env:PYTHONUTF8=1
26
+ # Classic cohort (auto-detected):
27
+ uv run gflow image t2i "a red apple on a rustic wooden table" --profile denon82 --aspect 16:9 --json
28
+ uv run gflow video t2v "a golden sunset over calm ocean waves" --profile denon82 --aspect 16:9 --duration 4 --json
29
+
30
+ # Agentic cohort, forced deterministically (clicks the Agent toggle):
31
+ $env:GFLOW_CLI_FORCE_AGENT_UI="1"
32
+ uv run gflow image t2i "a single ripe banana on a white plate" --profile denon82 --aspect 16:9 --json
33
+ uv run gflow image t2i "three colorful macarons on a marble surface" --profile denon82 --count 3 --json
34
+
35
+ # Deterministic harness (verifies forced→agentic→scrape→download, asserts file count):
36
+ .\scripts\e2e\agentic_image_e2e.ps1 -Profile denon82 -Count 3
37
+ ```
38
+
39
+ ## Evidence ledger (5-layer)
40
+
41
+ Each run: **file written** + **magic bytes (JPEG/MP4)** + **dimensions/size** +
42
+ **structlog invariants** + **user-confirmable artifact (media UUID + path)**.
43
+
44
+ | # | Path | Cohort | Result | Media UUID(s) | Bytes | Key structlog invariants |
45
+ |---|---|---|---|---|---|---|
46
+ | 1 | image t2i (classic) | classic | exit 0, 1 JPG | `f3e529ca…` | 647 KB | `ui_driver.bound mode=classic`; `crop_16_9` matched; `batchGenerateImages` 200 captured |
47
+ | 2 | video t2v (classic) | classic | exit 0, 1 MP4 | `433a92d5…` | 6.33 MB | `video_mode_entered`; `poll_terminal STATUS_SUCCESSFUL`; `video_saved` |
48
+ | 3 | image t2i (forced agentic, count 1) | agentic | exit 0, 1 JPG | `3722b716…` | 420 KB | `agent_mode_forced activated=true`; `ui_driver.bound mode=agentic`; full-res `getMediaUrlRedirect` URL |
49
+ | 4 | image t2i (forced agentic, count 3) | agentic | exit 0, **3 distinct** JPGs | `0bfb4127…`, `2f5c5f9e…`, `cd8f4781…` | 596 / 590 / 629 KB | `mode=agentic`; **dedup**: multiple `<img>` nodes → 3 distinct media UUIDs |
50
+
51
+ ## What this proves
52
+
53
+ - **Classic path unaffected** by the driver refactor (rows 1–2): image + video
54
+ generate and download end-to-end through the new `ClassicFlowUiDriver`.
55
+ - **Agentic image generation works end-to-end** (rows 3–4): the Agent toggle
56
+ flips the composer to agentic, the agentic driver encodes settings into the
57
+ prompt, types into the Slate composer, **scrapes** the generated assets from
58
+ the DOM (page-level network capture is dead in this cohort — Web-Worker
59
+ delegated), **deduplicates by media UUID**, and downloads via the same-origin
60
+ `labs.google/.../getMediaUrlRedirect` redirect (allow-listed for download).
61
+ - **Synthetic `GeneratedImage` fields** (`seed=0`, `workflow_id=""`,
62
+ `dimensions=(0,0)`) flow through the save/report path without error.
63
+
64
+ ## Not verified this cycle (recorded honestly)
65
+
66
+ - **A natural (non-forced) agentic load end-to-end:** the cohort flapped to
67
+ classic during the windows tested; the forced path exercises identical driver
68
+ code, but a naturally-served agentic load was not captured generating.
69
+ - **A positive content-policy refusal in the agentic cohort:** detection is
70
+ scoped to alert/dialog regions pending a captured sample (a chat-message-only
71
+ refusal currently misses → timeout). See `docs/AGENT_UI_E2E.md`.
72
+ - **Agentic video:** out of scope — raises `FlowAgentUiError` (exit 25) pending a
73
+ video scraping capture.
74
+
75
+ See [docs/AGENT_UI_E2E.md](AGENT_UI_E2E.md) for the repeatable procedure and the
76
+ three render-race / detection bugs this live testing surfaced and fixed.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gflow-cli"
3
- version = "0.19.0"
3
+ version = "0.20.0"
4
4
  description = "Unofficial CLI for Google Flow — drive Veo image-to-video generations from the terminal."
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env python3
2
+ r"""Offline analyzer for gflow-agent-browser-spike captures.
3
+
4
+ Reads the sandbox's agentui-capture-*.json files, classifies each composer
5
+ state, redacts + fingerprints the gating signals, diffs them across runs
6
+ (account / locale / engine), and emits a consolidated, redacted findings JSON.
7
+ No browser driving — pure file processing. Raw captures stay in the sandbox.
8
+
9
+ Usage:
10
+ .venv\Scripts\python.exe scripts\dev\analyze_agent_ui_capture.py \
11
+ path\to\agentui-capture-ffroliva-en-*.json \
12
+ path\to\agentui-capture-denon82-pt-*.json --out findings.json
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import argparse
18
+ import hashlib
19
+ import json
20
+ from dataclasses import dataclass
21
+ from enum import StrEnum
22
+ from pathlib import Path
23
+ from typing import Any
24
+
25
+
26
+ class ComposerState(StrEnum):
27
+ CLASSIC_MEDIA = "classic_media"
28
+ AGENT_OVER_CLASSIC = "agent_over_classic"
29
+ FORCED_AGENT = "forced_agent"
30
+ UNKNOWN = "unknown"
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class ComposerSignals:
35
+ crop_present: bool
36
+ agent_pill_present: bool
37
+ agent_chat_panel_present: bool
38
+ crop_recoverable: bool | None # None = recovery not attempted
39
+
40
+
41
+ def classify_composer(s: ComposerSignals) -> ComposerState:
42
+ if s.crop_present:
43
+ return ComposerState.CLASSIC_MEDIA
44
+ if not (s.agent_pill_present or s.agent_chat_panel_present):
45
+ return ComposerState.UNKNOWN
46
+ if s.crop_recoverable is True:
47
+ return ComposerState.AGENT_OVER_CLASSIC
48
+ if s.crop_recoverable is False:
49
+ return ComposerState.FORCED_AGENT
50
+ return ComposerState.UNKNOWN
51
+
52
+
53
+ def _sha8(value: str) -> str:
54
+ return hashlib.sha256(value.encode("utf-8")).hexdigest()[:8] if value else ""
55
+
56
+
57
+ def fingerprint_map(kv: dict[str, Any]) -> dict[str, str]:
58
+ """Reduce a {key: value} map (localStorage etc.) to {key: sha8(value)} —
59
+ lets us diff by presence + value-hash without persisting secrets."""
60
+ return {k: _sha8(str(v)) for k, v in kv.items()}
61
+
62
+
63
+ def diff_signal_sets(a: dict[str, str], b: dict[str, str]) -> dict[str, list[str]]:
64
+ ka, kb = set(a), set(b)
65
+ return {
66
+ "onlyInA": sorted(ka - kb),
67
+ "onlyInB": sorted(kb - ka),
68
+ "changed": sorted(k for k in ka & kb if a[k] != b[k]),
69
+ }
70
+
71
+
72
+ def load_capture(path: Path) -> dict[str, Any]:
73
+ return json.loads(path.read_text(encoding="utf-8"))
74
+
75
+
76
+ def _signals_from_capture(capture: dict[str, Any]) -> ComposerSignals:
77
+ s = capture.get("signals", {})
78
+ rec = s.get("cropRecoverable")
79
+ return ComposerSignals(
80
+ crop_present=bool(s.get("cropPresent")),
81
+ agent_pill_present=bool(s.get("agentPill")),
82
+ agent_chat_panel_present=bool(s.get("chatPanel")),
83
+ crop_recoverable=None if rec is None else bool(rec),
84
+ )
85
+
86
+
87
+ def summarize_capture(capture: dict[str, Any]) -> dict[str, Any]:
88
+ gating = capture.get("gating", {})
89
+ return {
90
+ "key": f"{capture.get('profile')}/{capture.get('locale')}",
91
+ "profile": capture.get("profile"),
92
+ "locale": capture.get("locale"),
93
+ "engine": capture.get("engine"),
94
+ "navigatorWebdriver": capture.get("navigatorWebdriver"),
95
+ "state": classify_composer(_signals_from_capture(capture)).value,
96
+ "localStorageFp": fingerprint_map(gating.get("localStorage", {})),
97
+ "sessionStorageFp": fingerprint_map(gating.get("sessionStorage", {})),
98
+ "cookieNames": sorted(gating.get("documentCookieNames", [])),
99
+ "nextDataPagePropKeys": sorted(gating.get("nextDataPagePropKeys", [])),
100
+ }
101
+
102
+
103
+ def build_findings(summaries: list[dict[str, Any]]) -> dict[str, Any]:
104
+ """Consolidate per-run summaries; diff the first agentic run against the
105
+ first classic run to surface candidate gating signals."""
106
+ states = {s["key"]: s["state"] for s in summaries}
107
+ agentic = next(
108
+ (s for s in summaries if s["state"] in ("forced_agent", "agent_over_classic")), None
109
+ )
110
+ classic = next((s for s in summaries if s["state"] == "classic_media"), None)
111
+ out: dict[str, Any] = {"runs": summaries, "states": states}
112
+ if agentic and classic:
113
+ out["localStorageDiff"] = diff_signal_sets(
114
+ agentic["localStorageFp"], classic["localStorageFp"]
115
+ )
116
+ out["sessionStorageDiff"] = diff_signal_sets(
117
+ agentic["sessionStorageFp"], classic["sessionStorageFp"]
118
+ )
119
+ out["cookieNameDiff"] = diff_signal_sets(
120
+ {k: "1" for k in agentic["cookieNames"]}, {k: "1" for k in classic["cookieNames"]}
121
+ )
122
+ out["nextDataKeyDiff"] = diff_signal_sets(
123
+ {k: "1" for k in agentic["nextDataPagePropKeys"]},
124
+ {k: "1" for k in classic["nextDataPagePropKeys"]},
125
+ )
126
+ else:
127
+ out["note"] = "need at least one agentic and one classic capture to diff gating signals"
128
+ return out
129
+
130
+
131
+ def main(argv: list[str] | None = None) -> int:
132
+ p = argparse.ArgumentParser(description="Analyze agentic-UI captures")
133
+ p.add_argument("captures", nargs="+", help="paths to agentui-capture-*.json")
134
+ p.add_argument("--out", default=None)
135
+ args = p.parse_args(argv)
136
+ summaries = [summarize_capture(load_capture(Path(c))) for c in args.captures]
137
+ findings = build_findings(summaries)
138
+ text = json.dumps(findings, indent=2)
139
+ if args.out:
140
+ Path(args.out).write_text(text, encoding="utf-8")
141
+ print(text)
142
+ return 0
143
+
144
+
145
+ if __name__ == "__main__":
146
+ raise SystemExit(main())