gflow-cli 0.25.0__tar.gz → 0.26.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 (502) hide show
  1. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/CHANGELOG.md +20 -0
  2. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/PKG-INFO +1 -1
  3. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/INDEX.md +1 -1
  4. gflow_cli-0.26.0/docs/LIVE_VERIFICATION_v0.26.0.md +69 -0
  5. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/MCP.md +1 -1
  6. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/pyproject.toml +1 -1
  7. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/__init__.py +1 -1
  8. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/dto.py +35 -2
  9. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/image.py +11 -2
  10. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/ui_automation.py +10 -0
  11. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/ui_automation_video.py +115 -0
  12. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/recorder.py +6 -1
  13. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/mcp/tools.py +53 -11
  14. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/worker/daemon.py +13 -1
  15. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_image_dto.py +30 -0
  16. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/mcp/test_tools_helpers.py +36 -5
  17. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/mcp/test_tools_wired.py +1 -0
  18. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/worker/test_daemon.py +1 -0
  19. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/uv.lock +1 -1
  20. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/README.md +0 -0
  21. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/active.md +0 -0
  22. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/branch-review.md +0 -0
  23. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/changelog.md +0 -0
  24. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/check.md +0 -0
  25. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/doc-review.md +0 -0
  26. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/issue-assessment.md +0 -0
  27. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/issue-resolve.md +0 -0
  28. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/known-issues.md +0 -0
  29. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/next.md +0 -0
  30. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/plan.md +0 -0
  31. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/pr-council-review.md +0 -0
  32. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/predict.md +0 -0
  33. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/release.md +0 -0
  34. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/scenario.md +0 -0
  35. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/sonar.md +0 -0
  36. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.claude/commands/gflow/status.md +0 -0
  37. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.env.template +0 -0
  38. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.gitattributes +0 -0
  39. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.github/CODEOWNERS +0 -0
  40. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  41. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.github/copilot-instructions.md +0 -0
  42. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.github/dependabot.yml +0 -0
  43. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.github/workflows/ci.yml +0 -0
  44. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.github/workflows/external-pr-triage.yml +0 -0
  45. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.github/workflows/governance-advisory.yml +0 -0
  46. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.github/workflows/governance-benchmark.yml +0 -0
  47. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.github/workflows/main-base-guard.yml +0 -0
  48. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.github/workflows/release.yml +0 -0
  49. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.gitignore +0 -0
  50. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.gitleaks.toml +0 -0
  51. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.pre-commit-config.yaml +0 -0
  52. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/.secrets.baseline +0 -0
  53. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/AGENTS.md +0 -0
  54. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/CLAUDE.md +0 -0
  55. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/CONFIGURATION.md +0 -0
  56. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/CONTRIBUTING.md +0 -0
  57. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/DISCLAIMER.md +0 -0
  58. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/GEMINI.md +0 -0
  59. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/KNOWN_ISSUES.md +0 -0
  60. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/LICENSE +0 -0
  61. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/PLAN.md +0 -0
  62. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/README.md +0 -0
  63. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/RELEASE.md +0 -0
  64. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/ROADMAP.md +0 -0
  65. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/conftest.py +0 -0
  66. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docker-compose.yml +0 -0
  67. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/AGENT_GUIDE.md +0 -0
  68. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/AGENT_UI_E2E.md +0 -0
  69. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/AGENT_UI_RECON.md +0 -0
  70. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/ARCHITECTURE.md +0 -0
  71. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/AUTHENTICATION.md +0 -0
  72. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/CHARACTER.md +0 -0
  73. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/CHARACTER_RECON.md +0 -0
  74. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/CONFIGURATION.md +0 -0
  75. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/DATA_LAYER.md +0 -0
  76. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/DEBUGGING.md +0 -0
  77. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/DEMOS.md +0 -0
  78. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/DEVELOPMENT.md +0 -0
  79. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/E2E_TESTING.md +0 -0
  80. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/EXTERNAL_STORAGE.md +0 -0
  81. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/GITHUB.md +0 -0
  82. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/GOVERNANCE_BENCHMARK.md +0 -0
  83. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/IMAGE_UPSCALE_RECON.md +0 -0
  84. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_data_layer.md +0 -0
  85. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_image_batch.md +0 -0
  86. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.10.0.md +0 -0
  87. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.11.0.md +0 -0
  88. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.12.0.md +0 -0
  89. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.13.0.md +0 -0
  90. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.16.0.md +0 -0
  91. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.17.0.md +0 -0
  92. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.18.0.md +0 -0
  93. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.19.0.md +0 -0
  94. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.20.0.md +0 -0
  95. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.20.1.md +0 -0
  96. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.21.0.md +0 -0
  97. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.22.0.md +0 -0
  98. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.23.0.md +0 -0
  99. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.24.0.md +0 -0
  100. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.25.0.md +0 -0
  101. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.7.0.md +0 -0
  102. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.8.1.md +0 -0
  103. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.9.0.md +0 -0
  104. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_v0.9.1.md +0 -0
  105. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/LIVE_VERIFICATION_video_download.md +0 -0
  106. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/MOVIE.md +0 -0
  107. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/PROJECT_STATUS.md +0 -0
  108. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/PROMPT_EXPANSION.md +0 -0
  109. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/SECURITY.md +0 -0
  110. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/TOOLS.md +0 -0
  111. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/USAGE.md +0 -0
  112. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/USER_GUIDE.md +0 -0
  113. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/assets/demo-split-pf.gif +0 -0
  114. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/assets/example-run.gif +0 -0
  115. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/assets/examples.webp +0 -0
  116. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/medium_tutorial.md +0 -0
  117. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/schemas/movie-handoff.schema.json +0 -0
  118. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/sonar-cleanup-tracker.md +0 -0
  119. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/superpowers/plans/2026-06-12-issue-174-library-ui-attach/PLAN.md +0 -0
  120. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/superpowers/plans/2026-06-22-mcp-server/PLAN.md +0 -0
  121. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/superpowers/plans/2026-06-22-mcp-server/PREDICT.md +0 -0
  122. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/superpowers/plans/2026-06-22-mcp-server/SCENARIO.md +0 -0
  123. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/superpowers/plans/2026-06-22-prompt-expansion/PLAN.md +0 -0
  124. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/superpowers/plans/2026-06-22-prompt-expansion/PREDICT.md +0 -0
  125. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/superpowers/plans/2026-06-22-prompt-expansion/SCENARIO.md +0 -0
  126. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/superpowers/plans/2026-06-24-gflow-studio-scaffold/PLAN.md +0 -0
  127. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/superpowers/plans/2026-06-24-rest-api-layer/PLAN.md +0 -0
  128. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/superpowers/plans/2026-06-26-mcp-e2e-test/PLAN.md +0 -0
  129. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/superpowers/research/2026-06-27-tool-abstraction-evaluation.md +0 -0
  130. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/docs/superpowers/specs/2026-06-29-issue-assessment-workflow-design.md +0 -0
  131. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/examples/README.md +0 -0
  132. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/examples/batch_from_config.py +0 -0
  133. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/examples/multi_prompt_t2i.py +0 -0
  134. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/examples/sample_config.json +0 -0
  135. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/examples/sample_prompts.txt +0 -0
  136. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/examples/single_image_t2i.py +0 -0
  137. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/examples/workflow_chain.py +0 -0
  138. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/llms.txt +0 -0
  139. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/README.md +0 -0
  140. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/01_upload_image.json +0 -0
  141. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/02_batchAsyncGenerateVideoText.json +0 -0
  142. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/03_batchCheckAsyncVideoGenerationStatus.json +0 -0
  143. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/04_archive_workflow.json +0 -0
  144. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/05_createProject.json +0 -0
  145. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/06_batchGenerateImages.json +0 -0
  146. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/07_batchGenerateImages_seeded.json +0 -0
  147. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/08_batchAsyncGenerateVideoStartAndEndImage.json +0 -0
  148. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/09_batchAsyncGenerateVideoReferenceImages.json +0 -0
  149. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/10_batchCheckAsyncVideoGenerationStatus_successful.json +0 -0
  150. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/11_batchCheckAsyncVideoGenerationStatus_failed.json +0 -0
  151. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/12_create_scene.json +0 -0
  152. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/13_sceneWorkflows_update.json +0 -0
  153. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/14_get_scene_workflows.json +0 -0
  154. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/samples/captured/15_commit_flowWorkflow.json +0 -0
  155. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/ci/check_doc_links.py +0 -0
  156. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/ci/check_materiality.py +0 -0
  157. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/ci/check_repo_hygiene.py +0 -0
  158. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/debug_editor.py +0 -0
  159. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/debug_gen_settings.py +0 -0
  160. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/debug_settings.py +0 -0
  161. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/_recording_client.py +0 -0
  162. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/_spike_common.py +0 -0
  163. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/active_plan.py +0 -0
  164. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/analyze_agent_ui_capture.py +0 -0
  165. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/capture_i2v_frame_slots_dom.py +0 -0
  166. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/capture_i2v_intercept_submit.py +0 -0
  167. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/capture_i2v_model_select_repro.py +0 -0
  168. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/capture_i2v_post_bind_state.py +0 -0
  169. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/capture_image_add_media_dom.py +0 -0
  170. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/capture_locale_invariants.py +0 -0
  171. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/cdp_drive_and_probe.py +0 -0
  172. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/character_create_spike.py +0 -0
  173. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/character_create_spike_v2.py +0 -0
  174. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/dump_character_selectors.js +0 -0
  175. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/make_project.py +0 -0
  176. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/materiality_backtest.py +0 -0
  177. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/monitor_pr_38.py +0 -0
  178. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/patch_character.py +0 -0
  179. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/skillopt/README.md +0 -0
  180. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/skillopt/harness.py +0 -0
  181. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/skillopt/tasks.json +0 -0
  182. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_char_editor_dom.py +0 -0
  183. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_char_gen_capture.py +0 -0
  184. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_image_upscale_capture.py +0 -0
  185. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_image_upscale_drive.py +0 -0
  186. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_image_upscale_recaptcha_action.py +0 -0
  187. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_image_upscale_rest_probe.py +0 -0
  188. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_issue170_picker_locale_recon.py +0 -0
  189. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_issue174_library_ui_recon.py +0 -0
  190. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_movie_attach_payload.py +0 -0
  191. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_movie_entity_recon.py +0 -0
  192. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_movie_gen_capture.py +0 -0
  193. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_movie_picker_select.py +0 -0
  194. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_movie_voice_list.py +0 -0
  195. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_patch_entity.py +0 -0
  196. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/dev/spike_patchright.py +0 -0
  197. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/diag/README.md +0 -0
  198. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/diag/capture_flow_traffic.py +0 -0
  199. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/diag/memory_profile.py +0 -0
  200. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/diag/recaptcha_mint.py +0 -0
  201. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/e2e/agentic_image_e2e.ps1 +0 -0
  202. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/e2e/capture_agent_toggle.py +0 -0
  203. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/record_demo.ps1 +0 -0
  204. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/smoke_image.py +0 -0
  205. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/smoke_real_chrome_image.py +0 -0
  206. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/smoke_video_editor.py +0 -0
  207. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/smoke_worker_style.py +0 -0
  208. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/scripts/verify_chrome_auth_viability.py +0 -0
  209. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/skills/README.md +0 -0
  210. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/skills/gflow-cli/SKILL.md +0 -0
  211. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/skills/issue-assessment/SKILL.md +0 -0
  212. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/skills/issue-resolve/SKILL.md +0 -0
  213. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/skills/plan/SKILL.md +0 -0
  214. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/skills/pr-council-review/SKILL.md +0 -0
  215. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/skills/predict/SKILL.md +0 -0
  216. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/skills/scenario/SKILL.md +0 -0
  217. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/skills/status/SKILL.md +0 -0
  218. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/sonar-project.properties +0 -0
  219. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/__main__.py +0 -0
  220. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/_cli_helpers.py +0 -0
  221. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/__init__.py +0 -0
  222. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/_engine.py +0 -0
  223. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/_retry.py +0 -0
  224. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/_sapisidhash.py +0 -0
  225. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/character.py +0 -0
  226. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/client.py +0 -0
  227. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/image_upscale.py +0 -0
  228. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/recaptcha.py +0 -0
  229. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/routes.py +0 -0
  230. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/scene.py +0 -0
  231. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/__init__.py +0 -0
  232. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/_common.py +0 -0
  233. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/_fingerprint.py +0 -0
  234. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/base.py +0 -0
  235. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/drivers/__init__.py +0 -0
  236. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/drivers/agentic.py +0 -0
  237. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/drivers/base.py +0 -0
  238. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/drivers/classic.py +0 -0
  239. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/drivers/factory.py +0 -0
  240. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/experimental/__init__.py +0 -0
  241. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/experimental/bearer.py +0 -0
  242. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py +0 -0
  243. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/transports/experimental/sapisidhash.py +0 -0
  244. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/api/video.py +0 -0
  245. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/auth/__init__.py +0 -0
  246. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/auth/base.py +0 -0
  247. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/auth/cookies.py +0 -0
  248. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/auth/factory.py +0 -0
  249. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/auth/internal_chromium.py +0 -0
  250. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/auth/real_chrome.py +0 -0
  251. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/auth/strategies.py +0 -0
  252. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/auth/verification.py +0 -0
  253. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/browser_manager.py +0 -0
  254. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/chain.py +0 -0
  255. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/chain_manifest.py +0 -0
  256. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/cli.py +0 -0
  257. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/cli_character.py +0 -0
  258. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/cli_data.py +0 -0
  259. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/cli_image.py +0 -0
  260. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/cli_models.py +0 -0
  261. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/cli_movie.py +0 -0
  262. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/cli_run.py +0 -0
  263. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/cli_scene.py +0 -0
  264. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/cli_tools.py +0 -0
  265. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/cli_video.py +0 -0
  266. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/composition.py +0 -0
  267. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/config.py +0 -0
  268. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/__init__.py +0 -0
  269. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/chain_repo.py +0 -0
  270. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/migrations/0001_initial.sql +0 -0
  271. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/migrations/0002_add_cloud_storage.sql +0 -0
  272. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/migrations/0003_add_scene_tables.sql +0 -0
  273. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/migrations/0004_add_scene_output_path.sql +0 -0
  274. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/migrations/0005_add_chain_links.sql +0 -0
  275. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/migrations/0006_add_operations_metadata.sql +0 -0
  276. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/migrations/0007_queue.sql +0 -0
  277. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/migrations/0008_add_operation_expanded_prompt.sql +0 -0
  278. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/migrations/__init__.py +0 -0
  279. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/models.py +0 -0
  280. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/queries.py +0 -0
  281. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/redaction.py +0 -0
  282. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/repository.py +0 -0
  283. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/data/store.py +0 -0
  284. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/errors.py +0 -0
  285. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/exceptions.py +0 -0
  286. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/image_batch.py +0 -0
  287. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/json_output.py +0 -0
  288. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/manifest.py +0 -0
  289. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/mcp/__init__.py +0 -0
  290. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/mcp/prompts.py +0 -0
  291. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/mcp/resources.py +0 -0
  292. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/mcp/server.py +0 -0
  293. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/media.py +0 -0
  294. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/movie_manifest.py +0 -0
  295. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/observability.py +0 -0
  296. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/paths.py +0 -0
  297. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/profile_store.py +0 -0
  298. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/services/__init__.py +0 -0
  299. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/services/character_create.py +0 -0
  300. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/storage.py +0 -0
  301. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/tools/__init__.py +0 -0
  302. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/tools/banned.py +0 -0
  303. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/tools/builtin/__init__.py +0 -0
  304. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/tools/builtin/creative-director.toml +0 -0
  305. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/tools/expander.py +0 -0
  306. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/tools/invocation.py +0 -0
  307. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/tools/loader.py +0 -0
  308. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/tools/registry.py +0 -0
  309. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/tools/runtime.py +0 -0
  310. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/tools/spec.py +0 -0
  311. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/ui/app.py +0 -0
  312. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/ui/server.py +0 -0
  313. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/src/gflow_cli/worker/queue.py +0 -0
  314. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tasks/lessons.md +0 -0
  315. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/test_assets/sample_batch.json +0 -0
  316. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/test_assets/sample_batch.tsv +0 -0
  317. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/test_assets/sample_batch_invalid.tsv +0 -0
  318. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/__init__.py +0 -0
  319. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/__init__.py +0 -0
  320. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/fixtures/character_gen_response.json +0 -0
  321. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/fixtures/patch_entity_response.json +0 -0
  322. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_aisandbox_auth_error.py +0 -0
  323. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_aisandbox_auth_headers.py +0 -0
  324. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_bearer_redaction.py +0 -0
  325. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_character.py +0 -0
  326. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_client.py +0 -0
  327. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_client_character.py +0 -0
  328. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_client_delete_characters.py +0 -0
  329. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_client_generate_character.py +0 -0
  330. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_client_image.py +0 -0
  331. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_client_launch_kwargs.py +0 -0
  332. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_client_patch_entity.py +0 -0
  333. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_client_scene.py +0 -0
  334. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_client_upscale.py +0 -0
  335. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_concurrency.py +0 -0
  336. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_dto.py +0 -0
  337. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_engine.py +0 -0
  338. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_image.py +0 -0
  339. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_image_upscale.py +0 -0
  340. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_post_json_aisandbox_auth.py +0 -0
  341. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_recaptcha.py +0 -0
  342. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_retry.py +0 -0
  343. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_routes.py +0 -0
  344. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_routes_character.py +0 -0
  345. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_routes_scene.py +0 -0
  346. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_sapisidhash_helper.py +0 -0
  347. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_scene_models.py +0 -0
  348. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_video.py +0 -0
  349. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/test_video_request.py +0 -0
  350. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/__init__.py +0 -0
  351. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/conftest.py +0 -0
  352. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/drivers/__init__.py +0 -0
  353. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/drivers/test_agentic.py +0 -0
  354. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/drivers/test_factory.py +0 -0
  355. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_base.py +0 -0
  356. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_bearer.py +0 -0
  357. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_common.py +0 -0
  358. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_evaluate_fetch.py +0 -0
  359. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_factory.py +0 -0
  360. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_fingerprint.py +0 -0
  361. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_sapisidhash.py +0 -0
  362. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_transport_timeout.py +0 -0
  363. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_ui_automation.py +0 -0
  364. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_ui_automation_batch.py +0 -0
  365. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_ui_automation_image_mode.py +0 -0
  366. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_ui_automation_video.py +0 -0
  367. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/api/transports/test_ui_character_editor.py +0 -0
  368. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/auth/strategies/test_factory.py +0 -0
  369. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/auth/strategies/test_strategies.py +0 -0
  370. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/auth/test_cookies.py +0 -0
  371. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/auth/test_verification.py +0 -0
  372. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/__init__.py +0 -0
  373. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_auth_list.py +0 -0
  374. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_character.py +0 -0
  375. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_character_create.py +0 -0
  376. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_data.py +0 -0
  377. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_image.py +0 -0
  378. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_image_seed_removed.py +0 -0
  379. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_image_selector_drift.py +0 -0
  380. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_image_upscale.py +0 -0
  381. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_models.py +0 -0
  382. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_movie.py +0 -0
  383. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_run.py +0 -0
  384. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_scene.py +0 -0
  385. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_tools.py +0 -0
  386. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_video.py +0 -0
  387. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_cli_video_chain.py +0 -0
  388. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_error_handling.py +0 -0
  389. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_helpers.py +0 -0
  390. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_movie_manifest.py +0 -0
  391. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_settings_validation.py +0 -0
  392. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/cli/test_t2i_multi_prompt.py +0 -0
  393. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/composition/test_character.py +0 -0
  394. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/composition/test_compose_prompt.py +0 -0
  395. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/composition/test_handoff.py +0 -0
  396. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/conftest.py +0 -0
  397. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/data/__init__.py +0 -0
  398. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/data/test_chain_repo.py +0 -0
  399. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/data/test_find_incomplete_character.py +0 -0
  400. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/data/test_models.py +0 -0
  401. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/data/test_packaging.py +0 -0
  402. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/data/test_recorder.py +0 -0
  403. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/data/test_recorder_character.py +0 -0
  404. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/data/test_redaction.py +0 -0
  405. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/data/test_repository.py +0 -0
  406. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/data/test_scene_persistence.py +0 -0
  407. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/data/test_settings_and_errors.py +0 -0
  408. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/data/test_store_migrations.py +0 -0
  409. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/dev/test_recording_client.py +0 -0
  410. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/__init__.py +0 -0
  411. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/conftest.py +0 -0
  412. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_aisandbox_auth_live.py +0 -0
  413. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_auth_verification_e2e.py +0 -0
  414. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_chain_e2e.py +0 -0
  415. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_character_create_e2e.py +0 -0
  416. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_daemon_e2e.py +0 -0
  417. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_data_layer_e2e.py +0 -0
  418. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_i2v_flags_e2e.py +0 -0
  419. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_image_batch_e2e.py +0 -0
  420. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_image_i2i_ref_cap_e2e.py +0 -0
  421. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_json_output_e2e.py +0 -0
  422. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_locale_selectors_e2e.py +0 -0
  423. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_scene_compose_live.py +0 -0
  424. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_tools_e2e.py +0 -0
  425. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_transports_e2e.py +0 -0
  426. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_video_r2v_ref_cap_e2e.py +0 -0
  427. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/e2e/test_video_t2v_e2e.py +0 -0
  428. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/__init__.py +0 -0
  429. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/auth.feature +0 -0
  430. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/auth_login.feature +0 -0
  431. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/character_create.feature +0 -0
  432. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/character_read.feature +0 -0
  433. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/conftest.py +0 -0
  434. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/image.feature +0 -0
  435. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/image_upscale.feature +0 -0
  436. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/locale_picker_include.feature +0 -0
  437. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/test_auth_login_steps.py +0 -0
  438. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/test_auth_steps.py +0 -0
  439. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/test_character_create_steps.py +0 -0
  440. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/test_character_read_steps.py +0 -0
  441. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/test_image_steps.py +0 -0
  442. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/test_image_upscale_steps.py +0 -0
  443. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/test_locale_picker_include_steps.py +0 -0
  444. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/test_step_collision_guard.py +0 -0
  445. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/test_video_agent_ui_steps.py +0 -0
  446. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/test_video_chain_steps.py +0 -0
  447. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/video_agent_ui.feature +0 -0
  448. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/features/video_chain.feature +0 -0
  449. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/fixtures/__init__.py +0 -0
  450. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/fixtures/seeded_catalog.py +0 -0
  451. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/image_batch/__init__.py +0 -0
  452. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/image_batch/test_image_manifest.py +0 -0
  453. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/image_batch/test_observability_events.py +0 -0
  454. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/integration/__init__.py +0 -0
  455. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/integration/conftest.py +0 -0
  456. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/integration/constants.py +0 -0
  457. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/integration/test_storage_gcs.py +0 -0
  458. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/integration/test_storage_s3.py +0 -0
  459. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/mcp/__init__.py +0 -0
  460. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/mcp/conftest.py +0 -0
  461. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/mcp/test_server.py +0 -0
  462. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/mcp/test_stdio_transport.py +0 -0
  463. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/scripts/test_analyze_agent_ui_capture.py +0 -0
  464. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/scripts/test_capture_locale_invariants.py +0 -0
  465. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/scripts/test_check_materiality.py +0 -0
  466. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/scripts/test_check_repo_hygiene.py +0 -0
  467. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/scripts/test_materiality_backtest.py +0 -0
  468. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/services/__init__.py +0 -0
  469. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/services/test_character_create_redaction.py +0 -0
  470. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/services/test_character_create_saga.py +0 -0
  471. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/services/test_character_gen_no_direct_post.py +0 -0
  472. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/smoke/__init__.py +0 -0
  473. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/smoke/test_profile_account_smoke.py +0 -0
  474. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/smoke/test_real_flow.py +0 -0
  475. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_auth.py +0 -0
  476. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_browser_manager.py +0 -0
  477. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_chain.py +0 -0
  478. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_chain_manifest.py +0 -0
  479. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_cli_data.py +0 -0
  480. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_config.py +0 -0
  481. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_conftest_isolation.py +0 -0
  482. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_data_queries.py +0 -0
  483. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_documentation_gate.py +0 -0
  484. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_errors.py +0 -0
  485. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_errors_403.py +0 -0
  486. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_json_output.py +0 -0
  487. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_manifest.py +0 -0
  488. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_marker_registry.py +0 -0
  489. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_media.py +0 -0
  490. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_observability.py +0 -0
  491. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_paths.py +0 -0
  492. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_profile_store.py +0 -0
  493. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/test_smoke.py +0 -0
  494. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/tools/__init__.py +0 -0
  495. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/tools/test_banned.py +0 -0
  496. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/tools/test_expander.py +0 -0
  497. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/tools/test_invocation.py +0 -0
  498. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/tools/test_loader.py +0 -0
  499. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/tools/test_registry.py +0 -0
  500. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/tools/test_runtime.py +0 -0
  501. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/tools/test_spec.py +0 -0
  502. {gflow_cli-0.25.0 → gflow_cli-0.26.0}/tests/ui/test_app.py +0 -0
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.26.0] — 2026-07-06
11
+
12
+ ### Added
13
+
14
+ - **Reference a generated image in `image i2i` by its Flow UUID (`gflow_generate_image`,
15
+ `reference_images`)**: pass a generated image's media UUID as a reference and gflow
16
+ attaches it by **selecting the already-existing asset in Flow's reference picker** —
17
+ it does **not** upload a duplicate copy (avoiding duplication is the preferred path).
18
+ The asset's tile is located by its media id in the thumbnail URL (robust to
19
+ display-name collisions), surfaced by a display-name search when it isn't already
20
+ visible, and attached in place. When the asset can't be located (e.g. it's in a
21
+ different project's picker), gflow falls back to uploading its on-disk local file. A
22
+ UUID that isn't in your catalog still attaches by media id (no error). Live-verified:
23
+ a generated image's UUID → i2i output that references it, with no duplicate upload.
24
+ - **Generated images now record their Flow display name**: the display name is extracted
25
+ from the `batchGenerateImages` response's `workflows[]` array (previously ignored) and
26
+ persisted in the asset catalog — the searchable label the media picker shows, and what
27
+ the UUID-reference path searches by. (Original find and approach by **@C1ph3r404**,
28
+ #253/#255.)
29
+
10
30
  ## [0.25.0] — 2026-07-06
11
31
 
12
32
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gflow-cli
3
- Version: 0.25.0
3
+ Version: 0.26.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
@@ -117,7 +117,7 @@ Slash commands for Claude Code, stored in `.claude/commands/gflow/`. All prefixe
117
117
  **"A gflow command hangs / fails — where do I start?"** → [DEBUGGING § Quick reference](DEBUGGING.md#quick-reference)
118
118
  **"Flow's UI broke a selector — how do I diagnose it?"** → [DEBUGGING § Inspecting Flow's live UI](DEBUGGING.md#inspecting-flows-live-ui)
119
119
  **"What does each `ui_automation.*` log event mean?"** → [DEBUGGING § Listener & HTTP-layer debugging](DEBUGGING.md#listener--http-layer-debugging)
120
- **"What was actually live-verified for the latest release?"** → latest: [LIVE_VERIFICATION_v0.25.0](LIVE_VERIFICATION_v0.25.0.md) (**#237 remote-UUID i2v proven live e2e GREEN** — a real 8s 720×1280 interpolation from a generated image's UUID, after the picker-search attach was reworked to local upload; #240 home-`.env` matrix live; plus two silent-failure guards root-caused live: video-as-image download rejection and rejected-upload fail-loud). Prior: [v0.24.0](LIVE_VERIFICATION_v0.24.0.md) (--project parity across CLI + MCP — automated coverage; live gen auth-gated). Prior: [v0.23.0](LIVE_VERIFICATION_v0.23.0.md) (MCP generation wiring proven live end-to-end — tool→worker→real Flow REST→structured-error translation; final image-write blocked by an expired session, environmental. #222 macOS fix reporter-verified e2e on Apple Silicon). Prior: [v0.22.0](LIVE_VERIFICATION_v0.22.0.md) (Tools framework — automated/CI coverage complete; credit/key-gated live feature run is a pending owner gate, see the doc) · [v0.21.0](LIVE_VERIFICATION_v0.21.0.md) (MCP server over stdio + HTTP/SSE — credit-free protocol handshake; 4 tools / 2 prompts / 3 resources confirmed on stdout) · [v0.20.1](LIVE_VERIFICATION_v0.20.1.md) · [v0.20.0](LIVE_VERIFICATION_v0.20.0.md) · [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)
120
+ **"What was actually live-verified for the latest release?"** → latest: [LIVE_VERIFICATION_v0.26.0](LIVE_VERIFICATION_v0.26.0.md) (**image i2i by generated-image UUID proven live e2e GREEN** — the existing asset is *selected in place* by its media UUID, no duplicate upload, producing an i2i output that references it; plus generated-image `display_name` capture, credited @C1ph3r404). Prior: [v0.25.0](LIVE_VERIFICATION_v0.25.0.md) (**#237 remote-UUID i2v proven live e2e GREEN** — a real 8s 720×1280 interpolation from a generated image's UUID, after the picker-search attach was reworked to local upload; #240 home-`.env` matrix live; plus two silent-failure guards root-caused live: video-as-image download rejection and rejected-upload fail-loud). Prior: [v0.24.0](LIVE_VERIFICATION_v0.24.0.md) (--project parity across CLI + MCP — automated coverage; live gen auth-gated). Prior: [v0.23.0](LIVE_VERIFICATION_v0.23.0.md) (MCP generation wiring proven live end-to-end — tool→worker→real Flow REST→structured-error translation; final image-write blocked by an expired session, environmental. #222 macOS fix reporter-verified e2e on Apple Silicon). Prior: [v0.22.0](LIVE_VERIFICATION_v0.22.0.md) (Tools framework — automated/CI coverage complete; credit/key-gated live feature run is a pending owner gate, see the doc) · [v0.21.0](LIVE_VERIFICATION_v0.21.0.md) (MCP server over stdio + HTTP/SSE — credit-free protocol handshake; 4 tools / 2 prompts / 3 resources confirmed on stdout) · [v0.20.1](LIVE_VERIFICATION_v0.20.1.md) · [v0.20.0](LIVE_VERIFICATION_v0.20.0.md) · [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)
121
121
  **"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.
122
122
  **"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)
123
123
  **"What was live-verified for the video-download feature (#29)?"** → [LIVE_VERIFICATION_video_download](LIVE_VERIFICATION_video_download.md)
@@ -0,0 +1,69 @@
1
+ # Live Verification — v0.26.0
2
+
3
+ Release date: 2026-07-06. Headline: **reference a generated image in `image i2i`
4
+ by its Flow UUID**, attached by **selecting the already-existing asset** in Flow's
5
+ reference picker rather than uploading a duplicate copy (the founder principle:
6
+ avoid duplication). Plus generated images now record their Flow **display name**
7
+ (the searchable picker label), extracted from the response's `workflows[]` array.
8
+
9
+ Live run: 2026-07-06, headed Playwright, profile `ffroliva`, real Flow.
10
+
11
+ ## Scope
12
+
13
+ | Change | Surface | Verdict |
14
+ |---|---|---|
15
+ | Image i2i UUID ref → **select existing asset** (no duplicate) | `mcp/tools.py`, `worker/daemon.py`, `api/image.py`, `api/transports/ui_automation*.py` | ✅ **Live e2e GREEN** |
16
+ | Generated-image `display_name` from `workflows[]` | `api/dto.py`, `data/recorder.py` | ✅ Unit-tested (real captured sample); credited @C1ph3r404 |
17
+
18
+ ## Mechanism
19
+
20
+ An `image i2i` `reference_images` entry that is a Flow media UUID is resolved at
21
+ enqueue time (best-effort, never errors — an uncatalogued UUID still attaches by
22
+ media id, PR #245) to the asset's `display_name` + on-disk `local_path`. The
23
+ transport then **prefers selecting the existing asset in place**:
24
+
25
+ 1. Locate the tile by the **media UUID in its thumbnail URL** (`img[src*=<uuid>]`)
26
+ — robust to display-name collisions, needs no search term.
27
+ 2. If not already visible, **search the display name** to surface it, then re-locate
28
+ by UUID.
29
+ 3. Attach in place. (The image picker attaches on tile-click and auto-closes; the
30
+ code also handles the video-style explicit "Add to Prompt" include.)
31
+ 4. **Fallback — local upload** only when the asset can't be located (e.g. it lives
32
+ in a different project's picker) and a local file exists.
33
+
34
+ ## Live end-to-end result — GREEN
35
+
36
+ `gflow_generate_image(prompt="the same rustic workshop wall, wider shot, more
37
+ tools", reference_images=["60dcb880-…" (a generated image's UUID)],
38
+ project="0f4e7eaa-…" (the asset's project), profile="ffroliva")` via the real
39
+ stdio MCP server. 5-layer ledger:
40
+
41
+ 1. **Attach path**: structlog `ui_automation_video.image_ref_selected_existing`
42
+ fired — and **no** `image_ref_upload_fallback` / `image_uploaded` event. The
43
+ existing asset was selected; **no duplicate was uploaded**.
44
+ 2. **Wire**: the generation ran the `batchGenerateImages` route carrying the
45
+ reference `imageInput` — the ref reached the generation, not a plain t2i.
46
+ 3. **File**: exactly one output written, `bee1e8a9-…_1.jpg`, 893 KB.
47
+ 4. **Magic bytes**: `ff d8 ff e0` (valid JPEG); task `status: completed`.
48
+ 5. **User-confirmable artifact**: the output shows the **same** rustic workshop
49
+ wall and the "v0.25.0" sign as the referenced image, as a wider shot with more
50
+ tools — the i2i reference visibly took.
51
+
52
+ ### Pre-fix note
53
+
54
+ The first live attempt selected the asset correctly but errored looking for a
55
+ separate "Add to Prompt" button — the image picker attaches on tile-click and
56
+ auto-closes (one step), unlike the video r2v picker. Fixed to treat an
57
+ auto-closed dialog as success; the re-run above is GREEN. (A hung Chrome holding
58
+ the profile's `SingletonLock` also caused two transient `launch_persistent_context`
59
+ failures between runs — cleared by killing the process + removing the lock.)
60
+
61
+ ## Automated coverage
62
+
63
+ - `_enrich_image_refs` unit tests (`tests/mcp/test_tools_helpers.py`): enriches a
64
+ catalogued ref with display_name + local_path; partial meta when no on-disk file;
65
+ uncatalogued UUID passes through with no error and no `ref_meta`.
66
+ - `display_name` extraction tests (`tests/api/test_image_dto.py`) against the real
67
+ captured `06_batchGenerateImages.json` sample.
68
+ - Full suite green (1994 passed; the one deselected `test_packaging` case is a
69
+ network-dependent sdist build, unrelated). `pyright src` 0 errors, ruff clean.
@@ -45,7 +45,7 @@ Both Click CLI (`src/gflow_cli/cli.py`) and MCP Server (`src/gflow_cli/mcp/`) ar
45
45
  The server registers three protocol surfaces:
46
46
 
47
47
  ### Tools (Executable actions)
48
- * `gflow_generate_image(prompt, model, aspect, count, seed, reference_images, tools, profile, project)`: Triggers text-to-image / image-to-image (Imagen / Nano Banana). `reference_images` switches to i2i; `project` generates into an existing Flow project id (mirrors CLI `--project`).
48
+ * `gflow_generate_image(prompt, model, aspect, count, seed, reference_images, tools, profile, project)`: Triggers text-to-image / image-to-image (Imagen / Nano Banana). `reference_images` switches to i2i and accepts **either a local file path or a generated image's Flow media UUID**. A UUID reference is attached by **selecting the already-existing asset in Flow's reference picker — no duplicate copy is uploaded** (locating the tile by the media id in its thumbnail URL, and searching the recorded display name to surface it when needed); gflow falls back to uploading the asset's on-disk local file only when it can't be located in place (e.g. it lives in a different project's picker). `project` generates into an existing Flow project id (mirrors CLI `--project`) — pass the reference's project to keep it selectable in place.
49
49
  * `gflow_generate_video(prompt, mode, aspect, initial_frame, end_frame, reference_images, tools, profile, project)`: Triggers vertical or landscape video generation (Veo). `mode` is `t2v`/`i2v`/`r2v`; `i2v` requires `initial_frame`, `r2v` requires `reference_images`; `project` generates into an existing Flow project id (mirrors CLI `--project`). `initial_frame`, `end_frame`, and `reference_images` each accept **either a local file path or the Flow image UUID of a generated asset** — pass a generated image's id straight in to chain image→video, and gflow attaches it for you (the UUID is resolved to the asset's already-on-disk local file and uploaded as the frame; no manual download/re-upload step). A UUID that isn't in your local asset catalog is rejected up front with a clear "Reference Not Found" error; a catalogued asset whose local file is no longer on disk gives a "Reference Not On Disk" error (re-generate it or pass a local path).
50
50
  * `gflow_list_projects(profile, limit)`: Queries SQLite catalog for recent generation folders.
51
51
  * `gflow_list_characters(profile)`: Lists Flow Character entities (requires an active browser session).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gflow-cli"
3
- version = "0.25.0"
3
+ version = "0.26.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" }
@@ -1,3 +1,3 @@
1
1
  """gflow-cli — unofficial CLI for Google Flow."""
2
2
 
3
- __version__ = "0.25.0"
3
+ __version__ = "0.26.0"
@@ -8,7 +8,7 @@ KeyErrors leak.
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
- from dataclasses import dataclass
11
+ from dataclasses import dataclass, replace
12
12
  from typing import TYPE_CHECKING, Any, Literal, cast
13
13
 
14
14
  if TYPE_CHECKING:
@@ -128,6 +128,10 @@ class GeneratedImage:
128
128
  fife_url: str # CDN URL — usually expires after ~6 hours
129
129
  dimensions: tuple[int, int] # (width, height)
130
130
  media_generation_id: str | None = None
131
+ # Flow-assigned display name (from the response's `workflows[]` array). This
132
+ # is the searchable label the media picker shows — recorded so a generated
133
+ # image can be referenced by name later. Original find by @C1ph3r404 (#253).
134
+ display_name: str | None = None
131
135
 
132
136
  @property
133
137
  def is_signed_url(self) -> bool:
@@ -172,8 +176,37 @@ class GeneratedImage:
172
176
  if not isinstance(media, list):
173
177
  msg = "unexpected batchGenerateImages response shape: media is not a list"
174
178
  raise ValueError(msg)
179
+ # Flow returns display names in a sibling `workflows[]` array keyed by
180
+ # workflow id (mirrors AssetInfo.from_upload_response). Build the lookup
181
+ # once and inject the name onto each parsed image. (Find by @C1ph3r404.)
182
+ workflow_names = cls._workflow_display_names(data.get("workflows"))
175
183
  items = cast("list[dict[str, Any]]", media)
176
- return [cls.from_response_item(item) for item in items]
184
+ results: list[GeneratedImage] = []
185
+ for item in items:
186
+ img = cls.from_response_item(item)
187
+ name = workflow_names.get(img.workflow_id)
188
+ if name:
189
+ img = replace(img, display_name=name)
190
+ results.append(img)
191
+ return results
192
+
193
+ @staticmethod
194
+ def _workflow_display_names(workflows: object) -> dict[str, str]:
195
+ """Map workflow id → displayName from the response's ``workflows[]``."""
196
+ names: dict[str, str] = {}
197
+ if not isinstance(workflows, list):
198
+ return names
199
+ for w in cast("list[Any]", workflows):
200
+ if not isinstance(w, dict):
201
+ continue
202
+ entry = cast("dict[str, Any]", w)
203
+ w_id = entry.get("name")
204
+ metadata = entry.get("metadata")
205
+ if isinstance(w_id, str) and isinstance(metadata, dict):
206
+ display_name = cast("dict[str, Any]", metadata).get("displayName")
207
+ if isinstance(display_name, str) and display_name:
208
+ names[w_id] = display_name
209
+ return names
177
210
 
178
211
 
179
212
  @dataclass(frozen=True)
@@ -185,11 +185,20 @@ class ImageRef:
185
185
  """A reference image for I2I, identified by the upload's media UUID.
186
186
 
187
187
  The wire shape is the dict returned by :meth:`to_wire`. The UUID comes
188
- from a prior ``/v1/flow/uploadImage`` call (see
189
- ``samples/captured/01_upload_image.json``).
188
+ from a prior ``/v1/flow/uploadImage`` call OR a generated image's media id
189
+ (see ``samples/captured/01_upload_image.json``).
190
+
191
+ ``display_name`` and ``local_path`` are UI-automation-only hints (ignored by
192
+ :meth:`to_wire`): they let the transport attach the ref by **selecting the
193
+ already-existing Flow asset in the picker** — located by ``name`` (the media
194
+ UUID) in the tile's image URL, surfaced by ``display_name`` when a search is
195
+ needed — and preferred over re-uploading a duplicate. ``local_path`` is the
196
+ fallback file to upload only when the asset can't be located in place.
190
197
  """
191
198
 
192
199
  name: str
200
+ display_name: str = ""
201
+ local_path: str = ""
193
202
 
194
203
  def __post_init__(self) -> None:
195
204
  # Reject empty, whitespace-only, AND whitespace-padded UUIDs.
@@ -2110,6 +2110,16 @@ class UiAutomationTransport(VideoGenerationMixin):
2110
2110
  if request.ref_paths:
2111
2111
  await self._attach_references(page, list(request.ref_paths), out_dir=out_dir)
2112
2112
 
2113
+ # Pre-generated image UUID refs: attach by selecting the EXISTING Flow
2114
+ # asset in the reference picker (no duplicate upload — founder principle),
2115
+ # falling back to uploading the local file only when it can't be located.
2116
+ if request.refs:
2117
+ await self._attach_image_uuid_refs(
2118
+ page,
2119
+ [(r.name, r.display_name, r.local_path) for r in request.refs],
2120
+ out_dir=out_dir,
2121
+ )
2122
+
2113
2123
  # Entity references: attach locked CHARACTER entities via the Personagens
2114
2124
  # picker (inherited from the video transport). The entity must live in the
2115
2125
  # project we generate in (pass --project / project_id). Flow's JS then
@@ -1479,6 +1479,121 @@ class VideoGenerationMixin:
1479
1479
  )
1480
1480
  log.info("ui_automation_video.remote_reference_attached", display_name=name)
1481
1481
 
1482
+ @staticmethod
1483
+ def _existing_asset_tile(page: Page, media_id: str) -> Locator:
1484
+ """Locate a picker tile for an already-existing asset by its media UUID.
1485
+
1486
+ The tile's thumbnail URL is ``media.getMediaUrlRedirect?name=<uuid>``, so
1487
+ matching ``img[src*=<uuid>]`` selects the exact asset with no dependence
1488
+ on a display name (robust to name collisions) and no search term.
1489
+ """
1490
+ return page.locator(f"[role='option']:has(img[src*='{media_id}'])").first
1491
+
1492
+ @staticmethod
1493
+ async def _select_existing_asset(
1494
+ page: Page,
1495
+ media_id: str,
1496
+ display_name: str,
1497
+ *,
1498
+ out_dir: Path | None,
1499
+ dialog_timeout_s: float = REMOTE_PICKER_CLOSE_TIMEOUT_S,
1500
+ ) -> bool:
1501
+ """In the OPEN reference picker, select the already-existing Flow asset
1502
+ identified by ``media_id`` (preferred over uploading a duplicate).
1503
+
1504
+ Locates the tile by the media UUID in its thumbnail URL; if it isn't
1505
+ already visible, searches ``display_name`` to surface it, then re-locates
1506
+ by UUID. Returns ``True`` once attached, ``False`` when the asset can't be
1507
+ located (the caller then falls back to a local upload).
1508
+ """
1509
+ tile = VideoGenerationMixin._existing_asset_tile(page, media_id)
1510
+ try:
1511
+ await tile.wait_for(state="visible", timeout=4000)
1512
+ except Exception: # noqa: BLE001 - not-visible is an expected branch
1513
+ if not display_name:
1514
+ return False
1515
+ search = page.locator(PICKER_SEARCH_INPUT)
1516
+ # Human-like typing jitter to dodge WAF heuristics — not security.
1517
+ await search.press_sequentially(display_name, delay=random.randint(10, 50)) # NOSONAR
1518
+ await page.wait_for_timeout(800)
1519
+ try:
1520
+ await tile.wait_for(state="visible", timeout=6000)
1521
+ except Exception: # noqa: BLE001 - still not found -> caller uploads
1522
+ return False
1523
+
1524
+ await tile.click()
1525
+ await page.wait_for_timeout(400)
1526
+
1527
+ # The image reference picker attaches on tile-click and auto-closes the
1528
+ # dialog (one step). The video r2v picker instead needs an explicit
1529
+ # "Add to Prompt" include after selecting. Handle both: if the dialog
1530
+ # already closed, the attach registered on click; otherwise resolve and
1531
+ # click the locale-safe include button.
1532
+ dialog = page.locator(DIALOG_ANY).last
1533
+ try:
1534
+ await dialog.wait_for(state="hidden", timeout=2500)
1535
+ return True
1536
+ except Exception: # noqa: BLE001 - still open -> needs explicit include
1537
+ pass
1538
+
1539
+ include = await VideoGenerationMixin._resolve_include_action(
1540
+ page,
1541
+ PICKER_INCLUDE_BUTTON,
1542
+ _INCLUDE_BUTTON_TIER_NAMES,
1543
+ surface="image_ref_include",
1544
+ detail=f"image ref {media_id}",
1545
+ out_dir=out_dir,
1546
+ screenshot_name="image_ref_include_missing.png",
1547
+ )
1548
+ await include.click(timeout=3000)
1549
+ await page.wait_for_timeout(600)
1550
+ try:
1551
+ await dialog.wait_for(state="hidden", timeout=dialog_timeout_s * 1000)
1552
+ except Exception as e:
1553
+ raise TransportTimeoutError(
1554
+ f"image ref {media_id} picker dialog did not close after "
1555
+ f"{dialog_timeout_s}s (the include action may not have registered)",
1556
+ ) from e
1557
+ return True
1558
+
1559
+ @staticmethod
1560
+ async def _attach_image_uuid_refs(
1561
+ page: Page,
1562
+ refs: list[tuple[str, str, str]],
1563
+ *,
1564
+ out_dir: Path | None,
1565
+ ) -> None:
1566
+ """Attach pre-generated image UUID references for I2I.
1567
+
1568
+ Each ref is ``(media_id, display_name, local_path)``. **Prefers selecting
1569
+ the already-existing Flow asset** in the picker (no duplicate upload —
1570
+ the founder principle); uploads ``local_path`` only as a fallback when the
1571
+ asset can't be located in place. Raises when neither is possible.
1572
+ """
1573
+ for media_id, display_name, local_path in refs:
1574
+ add = page.locator(ADD_MEDIA_BUTTON).first
1575
+ await add.wait_for(state="visible", timeout=8000)
1576
+ await add.click()
1577
+ await page.wait_for_timeout(800)
1578
+
1579
+ if await VideoGenerationMixin._select_existing_asset(
1580
+ page, media_id, display_name, out_dir=out_dir
1581
+ ):
1582
+ log.info("ui_automation_video.image_ref_selected_existing", media_id=media_id)
1583
+ continue
1584
+
1585
+ if local_path:
1586
+ log.info("ui_automation_video.image_ref_upload_fallback", media_id=media_id)
1587
+ await VideoGenerationMixin._upload_via_open_dialog(
1588
+ page, Path(local_path), log_label="image_ref", out_dir=out_dir
1589
+ )
1590
+ continue
1591
+
1592
+ raise TransportTimeoutError(
1593
+ f"image ref {media_id!r} could not be selected in the picker and "
1594
+ "has no local file to upload — re-generate it or pass a local path.",
1595
+ )
1596
+
1482
1597
  @staticmethod
1483
1598
  async def _scroll_picker_grid(page: Page, delta_px: int = PICKER_GRID_SCROLL_DELTA_PX) -> None:
1484
1599
  """Wheel-scroll the open resource picker down one step. The Tudo grid is
@@ -245,6 +245,11 @@ class OperationRecorder:
245
245
  media_type = mimetypes.guess_type(saved_path.name)[0]
246
246
  asset_id = _new_id()
247
247
  width, height = image.dimensions
248
+ # Persist the Flow-assigned display name (when present) so a generated
249
+ # image can later be referenced by name — the picker's searchable label.
250
+ metadata: dict[str, str] = {"fife_url": image.fife_url}
251
+ if image.display_name:
252
+ metadata["display_name"] = image.display_name
248
253
  repo.upsert_asset(
249
254
  AssetRecord(
250
255
  id=asset_id,
@@ -261,7 +266,7 @@ class OperationRecorder:
261
266
  height=height,
262
267
  duration_seconds=None,
263
268
  seed=image.seed,
264
- metadata_json=redact_metadata({"fife_url": image.fife_url}),
269
+ metadata_json=redact_metadata(metadata),
265
270
  ),
266
271
  )
267
272
  repo.link_operation_asset(op_id, asset_id, OperationAssetRole.OUTPUT, i)
@@ -386,28 +386,70 @@ def _resolve_ref_local_path(
386
386
 
387
387
 
388
388
  # Video task types whose media-id refs are resolved to local file paths and
389
- # attached via the local-upload path (#237 fix — see _resolve_ref_local_path).
390
- # Image task types attach remote refs by raw media id and MUST NOT be resolved
391
- # here gating on that was the PR #245 image-i2i regression.
389
+ # attached via the local-upload path (#237 fix — see _resolve_ref_local_path):
390
+ # the video FRAME picker does not surface generated media, so there is no
391
+ # existing tile to select. Image task types DO surface generated media in the
392
+ # reference picker, so their refs are enriched (not rewritten) and attached by
393
+ # selecting the existing asset — see _enrich_image_refs.
392
394
  _VIDEO_TASK_TYPES = frozenset({"t2v", "i2v", "r2v"})
393
395
 
394
396
 
397
+ def _enrich_image_refs(
398
+ data_repo: DataRepository,
399
+ profile: str,
400
+ payload: dict[str, Any],
401
+ ) -> None:
402
+ """Annotate each media-id ref in an image ``payload`` with its Flow
403
+ ``display_name`` and on-disk ``local_path`` (best-effort), so the transport
404
+ can attach it by **selecting the already-existing Flow asset** in the
405
+ reference picker — the preferred path (no duplicate upload) — with the local
406
+ file as an upload fallback. Never errors: an uncatalogued UUID is still a
407
+ valid media id to attach in place (PR #245 — image refs pass through).
408
+ """
409
+ refs = payload.get("refs")
410
+ if not refs:
411
+ return
412
+ meta: dict[str, dict[str, str]] = {}
413
+ for ref in refs:
414
+ asset = data_repo.get_asset_by_any_id(profile, ref)
415
+ if asset is None:
416
+ continue
417
+ entry: dict[str, str] = {}
418
+ name = asset.metadata_json.get("display_name")
419
+ if isinstance(name, str) and name:
420
+ entry["display_name"] = name
421
+ for local_file in asset.local_files:
422
+ if (
423
+ local_file.storage_provider is None
424
+ and local_file.path is not None
425
+ and local_file.path.is_file()
426
+ ):
427
+ entry["local_path"] = str(local_file.path)
428
+ break
429
+ if entry:
430
+ meta[ref] = entry
431
+ if meta:
432
+ payload["ref_meta"] = meta
433
+
434
+
395
435
  def _resolve_payload_refs(
396
436
  data_repo: DataRepository,
397
437
  profile: str,
398
438
  payload: dict[str, Any],
399
439
  task_type: str,
400
440
  ) -> dict[str, Any] | None:
401
- """Rewrite every media-id ref in ``payload`` to a local file path in place.
402
-
403
- Only the video task types are resolved (image tasks attach remote refs by
404
- raw media id and are left untouched PR #245). For video, a start/end frame
405
- ref becomes the local ``start_image``/``end_image`` path and r2v ``refs`` are
406
- resolved and merged into ``reference_images`` so the existing local-upload
407
- attach runs and no (broken) picker name search is attempted. Returns an error
408
- envelope on the first unresolvable UUID, else ``None``.
441
+ """Resolve media-id refs in ``payload`` in place, by task type.
442
+
443
+ Video task types: a start/end frame ref becomes the local
444
+ ``start_image``/``end_image`` path and r2v ``refs`` are merged into
445
+ ``reference_images`` (local-upload attach the video frame picker doesn't
446
+ surface generated media). Image task types: refs are *enriched* with
447
+ display_name/local_path so the transport selects the existing asset in the
448
+ picker (see _enrich_image_refs). Returns an error envelope on the first
449
+ unresolvable VIDEO UUID, else ``None`` (image enrichment never errors).
409
450
  """
410
451
  if task_type not in _VIDEO_TASK_TYPES:
452
+ _enrich_image_refs(data_repo, profile, payload)
411
453
  return None
412
454
  if "refs" in payload:
413
455
  resolved_paths: list[str] = []
@@ -291,7 +291,19 @@ class FlowWorker:
291
291
  model_val = payload.get("model")
292
292
  model = ImageModel.from_cli(model_val) if model_val else ImageModel.NARWHAL
293
293
 
294
- refs = tuple(ImageRef(r) for r in payload.get("refs", []))
294
+ # ref_meta (set by the MCP layer's _enrich_image_refs) carries the
295
+ # display_name + on-disk local_path per media-id ref, so the transport
296
+ # can select the EXISTING asset in the picker (preferred, no duplicate)
297
+ # and fall back to uploading local_path only if it can't be located.
298
+ ref_meta: dict[str, dict[str, str]] = payload.get("ref_meta", {})
299
+ refs = tuple(
300
+ ImageRef(
301
+ r,
302
+ display_name=ref_meta.get(r, {}).get("display_name", ""),
303
+ local_path=ref_meta.get(r, {}).get("local_path", ""),
304
+ )
305
+ for r in payload.get("refs", [])
306
+ )
295
307
  ref_paths = tuple(Path(p) for p in payload.get("ref_paths", []))
296
308
  # NOTE: the payload may carry "ref_names" (the MCP layer resolves them
297
309
  # for the video request); the image transport attaches remote refs by
@@ -68,6 +68,36 @@ class TestGeneratedImage:
68
68
  assert len(results) == 1
69
69
  assert results[0].seed == 646428
70
70
 
71
+ def test_from_response_dict_extracts_display_name_from_workflows(self) -> None:
72
+ """The Flow-assigned display name lives in the response's ``workflows[]``
73
+ array (keyed by workflow id), not in the media item — extract it onto
74
+ the GeneratedImage so the catalog records a searchable name. (Original
75
+ find by @C1ph3r404, PR #253.)"""
76
+ sample = _load("06_batchGenerateImages.json")
77
+ body = sample["response_body_parsed"]
78
+ results = GeneratedImage.from_response_dict(body)
79
+ assert results[0].display_name == "Warrior Zelda in dungeon"
80
+
81
+ def test_from_response_dict_display_name_none_when_no_workflows(self) -> None:
82
+ """A response without a matching workflow leaves display_name as None
83
+ (graceful — the field is optional)."""
84
+ item = {
85
+ "name": "m",
86
+ "workflowId": "w-1",
87
+ "image": {
88
+ "generatedImage": {
89
+ "seed": "1",
90
+ "prompt": "p",
91
+ "modelNameType": "NARWHAL",
92
+ "aspectRatio": "IMAGE_ASPECT_RATIO_PORTRAIT",
93
+ "fifeUrl": "https://flow-content.google/image/x",
94
+ },
95
+ "dimensions": {"width": 1, "height": 1},
96
+ },
97
+ }
98
+ results = GeneratedImage.from_response_dict({"media": [item]})
99
+ assert results[0].display_name is None
100
+
71
101
  def test_parse_seeded_landscape_response(self) -> None:
72
102
  """Seeded I2I + 4:3 landscape capture parses to expected dimensions/aspect."""
73
103
  sample = _load("07_batchGenerateImages_seeded.json")
@@ -158,9 +158,10 @@ class _LocalFile:
158
158
 
159
159
 
160
160
  class _Asset:
161
- def __init__(self, local_files, flow_media_id="m"):
161
+ def __init__(self, local_files, flow_media_id="m", metadata_json=None):
162
162
  self.local_files = list(local_files)
163
163
  self.flow_media_id = flow_media_id
164
+ self.metadata_json = metadata_json or {}
164
165
 
165
166
 
166
167
  class _FakeRepo:
@@ -264,9 +265,9 @@ def test_resolve_payload_refs_r2v_refs_merge_into_reference_images(tmp_path: Pat
264
265
  assert "ref_names" not in payload
265
266
 
266
267
 
267
- def test_resolve_payload_refs_leaves_image_refs_untouched() -> None:
268
- """PR #245 review #1: only the video path is resolved. Image 'refs' (media-id
269
- UUIDs) attach by id and must pass through untouched even for a missing UUID."""
268
+ def test_resolve_payload_refs_image_uncatalogued_uuid_passes_through() -> None:
269
+ """PR #245: an image 'refs' UUID not in the local catalog must pass through
270
+ without error (still a valid media id to attach in place); video errors."""
270
271
  from gflow_cli.mcp.tools import _resolve_payload_refs
271
272
 
272
273
  repo = _FakeRepo(asset=None) # UUID not in local catalog
@@ -274,9 +275,39 @@ def test_resolve_payload_refs_leaves_image_refs_untouched() -> None:
274
275
  # video task type: resolves (and would error on the missing UUID)
275
276
  err_video = _resolve_payload_refs(repo, "default", dict(payload), task_type="r2v")
276
277
  assert err_video is not None
277
- # image task type: passes through untouched, no error
278
+ # image task type: passes through, no error, no ref_meta (nothing to enrich)
278
279
  p_image = dict(payload)
279
280
  err_image = _resolve_payload_refs(repo, "default", p_image, task_type="i2i")
280
281
  assert err_image is None
281
282
  assert p_image["refs"] == [_A_UUID]
282
283
  assert "reference_images" not in p_image
284
+ assert "ref_meta" not in p_image
285
+
286
+
287
+ def test_resolve_payload_refs_image_enriches_found_ref(tmp_path: Path) -> None:
288
+ """An image 'refs' UUID that IS in the catalog is enriched with its
289
+ display_name and on-disk local_path (ref_meta) so the transport can select
290
+ the existing asset in the picker (prefer-existing), refs left in place."""
291
+ from gflow_cli.mcp.tools import _resolve_payload_refs
292
+
293
+ img = tmp_path / "gen.png"
294
+ img.write_bytes(b"\x89PNG\r\n\x1a\n")
295
+ repo = _FakeRepo(asset=_Asset([_LocalFile(img)], metadata_json={"display_name": "Cozy cabin"}))
296
+ payload = {"refs": [_A_UUID]}
297
+ err = _resolve_payload_refs(repo, "default", payload, task_type="i2i")
298
+ assert err is None
299
+ assert payload["refs"] == [_A_UUID] # not rewritten
300
+ assert payload["ref_meta"] == {_A_UUID: {"display_name": "Cozy cabin", "local_path": str(img)}}
301
+
302
+
303
+ def test_resolve_payload_refs_image_enrich_partial_meta_only() -> None:
304
+ """Enrichment records only what's available — a catalogued asset with no
305
+ on-disk file contributes just its display_name (picker-select still works;
306
+ no local-upload fallback for that ref)."""
307
+ from gflow_cli.mcp.tools import _resolve_payload_refs
308
+
309
+ repo = _FakeRepo(asset=_Asset([], metadata_json={"display_name": "Named but pruned"}))
310
+ payload = {"refs": [_A_UUID]}
311
+ err = _resolve_payload_refs(repo, "default", payload, task_type="i2i")
312
+ assert err is None
313
+ assert payload["ref_meta"] == {_A_UUID: {"display_name": "Named but pruned"}}
@@ -51,6 +51,7 @@ class _FakeImage:
51
51
  aspect_ratio: str = "1:1"
52
52
  seed: int = 12345
53
53
  fife_url: str = "http://fake"
54
+ display_name: str | None = None # mirrors GeneratedImage.display_name
54
55
 
55
56
 
56
57
  def _completed_video_result(media_id: str = "media-vid-wired") -> VideoResult:
@@ -25,6 +25,7 @@ class FakeGeneratedImage:
25
25
  aspect_ratio: str = "1:1"
26
26
  seed: int = 12345
27
27
  fife_url: str = "http://fake"
28
+ display_name: str | None = None # mirrors GeneratedImage.display_name
28
29
 
29
30
 
30
31
  def _completed_video_result(media_id: str = "media-vid-123") -> VideoResult:
@@ -787,7 +787,7 @@ wheels = [
787
787
 
788
788
  [[package]]
789
789
  name = "gflow-cli"
790
- version = "0.25.0"
790
+ version = "0.26.0"
791
791
  source = { editable = "." }
792
792
  dependencies = [
793
793
  { name = "browser-cookie3" },
File without changes