devops-bot-sdk 1.6.32__tar.gz → 1.6.33__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 (163) hide show
  1. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/__init__.py +2 -2
  4. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/self_report.py +33 -14
  5. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_self_report.py +23 -1
  6. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/README.md +0 -0
  7. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
  8. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  9. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  10. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/devops_bot_sdk.egg-info/requires.txt +0 -0
  11. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  12. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/pyproject.toml +0 -0
  13. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agentd.py +0 -0
  14. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/__init__.py +0 -0
  15. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/activity.py +0 -0
  16. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/builder.py +0 -0
  17. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/chat.py +0 -0
  18. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/connectors.py +0 -0
  19. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/engines/__init__.py +0 -0
  20. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/engines/claude_code.py +0 -0
  21. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/engines/http.py +0 -0
  22. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/engines/mcp.py +0 -0
  23. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/engines/n8n.py +0 -0
  24. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/engines/shell.py +0 -0
  25. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/integrations.py +0 -0
  26. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/inventory.py +0 -0
  27. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/library/__init__.py +0 -0
  28. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/manifest.py +0 -0
  29. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/prompt_studio.py +0 -0
  30. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/prompt_template.py +0 -0
  31. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/prompt_vars.py +0 -0
  32. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/run_log.py +0 -0
  33. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/schedules.py +0 -0
  34. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/secrets.py +0 -0
  35. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/ticket_run.py +0 -0
  36. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/usage.py +0 -0
  37. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/agents/varstore.py +0 -0
  38. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/browser_profile.py +0 -0
  39. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/cli.py +0 -0
  40. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/client.py +0 -0
  41. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/collectors/__init__.py +0 -0
  42. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/collectors/files.py +0 -0
  43. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/collectors/process.py +0 -0
  44. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/collectors/screenshot.py +0 -0
  45. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/config.py +0 -0
  46. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/crucial.py +0 -0
  47. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/departments.py +0 -0
  48. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/design_verify/__init__.py +0 -0
  49. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/design_verify/assets.py +0 -0
  50. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/design_verify/bootstrap.py +0 -0
  51. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/design_verify/browser.py +0 -0
  52. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/design_verify/compare.py +0 -0
  53. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/design_verify/loop.py +0 -0
  54. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/exceptions.py +0 -0
  55. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/formats.py +0 -0
  56. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/git_ops.py +0 -0
  57. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/github_ingest.py +0 -0
  58. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/graph_recon.py +0 -0
  59. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/graphify.py +0 -0
  60. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/hooks/__init__.py +0 -0
  61. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/hooks/crucial_guard.py +0 -0
  62. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/intake.py +0 -0
  63. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ipc/__init__.py +0 -0
  64. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ipc/electron_bridge.py +0 -0
  65. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ipc/handlers.py +0 -0
  66. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/local_exec.py +0 -0
  67. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/models/__init__.py +0 -0
  68. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/models/envelope.py +0 -0
  69. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/models/requests.py +0 -0
  70. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/models/responses.py +0 -0
  71. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/models/snapshots.py +0 -0
  72. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/phase_timer.py +0 -0
  73. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/plan_run.py +0 -0
  74. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/platform_compat.py +0 -0
  75. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/proof_readers.py +0 -0
  76. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/py.typed +0 -0
  77. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/run_auto.py +0 -0
  78. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/scheduler.py +0 -0
  79. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/self_report_local.py +0 -0
  80. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/sse.py +0 -0
  81. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/task_file.py +0 -0
  82. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/test.py +0 -0
  83. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/test_pipeline.py +0 -0
  84. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ticket_fields.py +0 -0
  85. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/__init__.py +0 -0
  86. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/api.py +0 -0
  87. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/dashboard.py +0 -0
  88. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/health.py +0 -0
  89. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/ip_allowlist.py +0 -0
  90. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/jobs.py +0 -0
  91. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/models.py +0 -0
  92. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/runner.py +0 -0
  93. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/security.py +0 -0
  94. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/server.py +0 -0
  95. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/service.py +0 -0
  96. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/session.py +0 -0
  97. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/setup_manager.py +0 -0
  98. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/agents.html +0 -0
  99. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/app.js +0 -0
  100. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/chat.html +0 -0
  101. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/custom-agents.html +0 -0
  102. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/default-agents.html +0 -0
  103. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/index.html +0 -0
  104. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/login.html +0 -0
  105. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/report.html +0 -0
  106. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/runs.html +0 -0
  107. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/schedules.html +0 -0
  108. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/setup.html +0 -0
  109. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/styles.css +0 -0
  110. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/ui/static/tickets.html +0 -0
  111. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/updater.py +0 -0
  112. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/url_access.py +0 -0
  113. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/sdk/vps_health.py +0 -0
  114. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/setup.cfg +0 -0
  115. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_agent_activity.py +0 -0
  116. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_agent_activity_tracking.py +0 -0
  117. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_agent_chat_features.py +0 -0
  118. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_agent_listing.py +0 -0
  119. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_agent_prompt_async.py +0 -0
  120. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_agent_prompt_models.py +0 -0
  121. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_agent_run_ticket.py +0 -0
  122. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_agent_schedules.py +0 -0
  123. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_agent_workspace_settings.py +0 -0
  124. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_agents_connectors_builder.py +0 -0
  125. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_allowed_tools_config.py +0 -0
  126. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_auto_agent_dispatch.py +0 -0
  127. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_browser_profile.py +0 -0
  128. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_client_jira_projects.py +0 -0
  129. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_config_token_diagnostics.py +0 -0
  130. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_configure_token_owner.py +0 -0
  131. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_console_encoding.py +0 -0
  132. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_departments.py +0 -0
  133. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_epic_decompose_run.py +0 -0
  134. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_epic_inherited_agent_id.py +0 -0
  135. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_epic_session_isolation.py +0 -0
  136. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_formats.py +0 -0
  137. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_git_action.py +0 -0
  138. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_github_ingest.py +0 -0
  139. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_graph_recon.py +0 -0
  140. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_graphify_build.py +0 -0
  141. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_graphify_hook_install.py +0 -0
  142. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_health.py +0 -0
  143. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_intake_plan_review.py +0 -0
  144. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_otp_login.py +0 -0
  145. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_phase_timer.py +0 -0
  146. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_plan_new_command.py +0 -0
  147. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_plan_run_activity.py +0 -0
  148. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_proof_readers.py +0 -0
  149. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_recon_session_isolation.py +0 -0
  150. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_run_auto_agent_only.py +0 -0
  151. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_run_outputs.py +0 -0
  152. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_schedule_api.py +0 -0
  153. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_scheduler.py +0 -0
  154. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_service_token_warning.py +0 -0
  155. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_task_file_and_plan_run.py +0 -0
  156. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_task_file_format_doc.py +0 -0
  157. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_ticket_fields.py +0 -0
  158. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_ticket_run_resolve_by_id.py +0 -0
  159. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_ticket_speed_fixes.py +0 -0
  160. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_update_timer.py +0 -0
  161. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_updater_extras.py +0 -0
  162. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_url_access.py +0 -0
  163. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.33}/tests/test_vps_health.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.6.32
3
+ Version: 1.6.33
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.6.32
3
+ Version: 1.6.33
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,6 +1,6 @@
1
1
  """AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
2
2
 
3
- Version: 1.6.32
3
+ Version: 1.6.33
4
4
 
5
5
  Public surface:
6
6
  BackendClient.from_config() — create client from ~/.agentos/config.toml
@@ -34,7 +34,7 @@ Rules:
34
34
  - All data egress through submit_webhook only
35
35
  """
36
36
 
37
- __version__ = "1.6.32" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
37
+ __version__ = "1.6.33" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
38
38
  __author__ = "AgentOS" # sdk.client.SDK_VERSION and the UI server version all read this.
39
39
 
40
40
  from sdk.client import BackendClient
@@ -467,9 +467,17 @@ async def submit_and_evaluate(
467
467
  # PR, an Outlook thread or an internal dashboard comes back 404 and is
468
468
  # scored `unverifiable` — the work punished for where its evidence lives.
469
469
  # This machine is the one with the credentials, so it reads the page and
470
- # attaches what it read; the URL still travels too, so the record keeps
471
- # the link a human would click.
472
- extra, notes = await _resolve_link_proofs(client, urls)
470
+ # sends what it read INSTEAD of the link.
471
+ #
472
+ # Instead, not as well as. Sending both means the backend still fetches
473
+ # the URL, still 404s on it, and the submission carries two contradictory
474
+ # judgements of one piece of evidence: the link "unverifiable" beside the
475
+ # text of that same link "supports". That is not merely noisy — the
476
+ # evaluator reads the phantom failure as missing evidence and marks the
477
+ # criterion PARTIAL for it, so a link that WAS read costs score for not
478
+ # having been read. A URL that could not be resolved here still travels,
479
+ # because the backend may reach something this machine cannot.
480
+ extra, notes, urls = await _resolve_link_proofs(client, urls)
473
481
  files.extend(extra)
474
482
  for note in notes:
475
483
  mirror(note)
@@ -671,15 +679,21 @@ def _link_slug(url: str) -> str:
671
679
 
672
680
  async def _resolve_link_proofs(
673
681
  client: Any, urls: list[str],
674
- ) -> tuple[list[tuple[str, bytes, str]], list[str]]:
675
- """Read each URL proof with this machine's credentials. ``(files, notes)``.
676
-
677
- Each link that opens becomes an extra ``text/plain`` file proof carrying the
678
- page and a header naming the source and the route that read it. That shape is
679
- deliberate: the backend already stores file proofs and already extracts them,
680
- so evidence the SDK gathered flows through exactly the pipeline evidence the
681
- person uploaded does — no new route, no new contract, and it keeps working
682
- against a backend that has never heard of this module.
682
+ ) -> tuple[list[tuple[str, bytes, str]], list[str], list[str]]:
683
+ """Read each URL proof with this machine's credentials.
684
+
685
+ Returns ``(files, notes, unresolved)`` — the pages that were read, anything
686
+ worth telling the operator, and the URLs to still send AS urls because
687
+ nothing here could open them.
688
+
689
+ Each link that opens becomes a ``text/plain`` file proof carrying the page and
690
+ a header naming the source and the route that read it, and is then dropped
691
+ from the URL list. That shape is deliberate: the backend already stores file
692
+ proofs and already extracts them, so evidence the SDK gathered flows through
693
+ exactly the pipeline evidence the person uploaded does — no new route, no new
694
+ contract, and it keeps working against a backend that has never heard of this
695
+ module. The link itself is not lost: it is the ``Source:`` line of the file
696
+ and the file's own name.
683
697
 
684
698
  Sequential, not gathered. The browser route drives ONE ``agent-browser``
685
699
  daemon through a stateful open-then-snapshot pair; two concurrent resolutions
@@ -690,7 +704,7 @@ async def _resolve_link_proofs(
690
704
  the behaviour that existed before this, so the worst case is the old one.
691
705
  """
692
706
  if not urls or not resolve_links_enabled():
693
- return [], []
707
+ return [], [], list(urls)
694
708
 
695
709
  from sdk import url_access
696
710
 
@@ -707,6 +721,7 @@ async def _resolve_link_proofs(
707
721
 
708
722
  files: list[tuple[str, bytes, str]] = []
709
723
  notes: list[str] = []
724
+ unresolved: list[str] = []
710
725
  loop = asyncio.get_running_loop()
711
726
  deadline = loop.time() + _RESOLVE_BUDGET_S
712
727
  for index, url in enumerate(urls, start=1):
@@ -715,6 +730,7 @@ async def _resolve_link_proofs(
715
730
  notes.append(f"Proof link {url} was left for the backend to fetch — the "
716
731
  f"{int(_RESOLVE_BUDGET_S)}s budget for reading links locally "
717
732
  f"was already spent.")
733
+ unresolved.append(url)
718
734
  continue
719
735
  try:
720
736
  res = await asyncio.wait_for(
@@ -723,15 +739,18 @@ async def _resolve_link_proofs(
723
739
  except asyncio.TimeoutError:
724
740
  logger.info("self_report.resolve_timeout url=%s", url)
725
741
  notes.append(f"Proof link {url} timed out being read locally.")
742
+ unresolved.append(url)
726
743
  continue
727
744
  except Exception as exc: # noqa: BLE001 — resolve_url is not supposed to
728
745
  logger.warning("self_report.resolve_failed url=%s err=%s", url, exc)
729
746
  notes.append(f"Proof link could not be read locally ({url}) — {exc}")
747
+ unresolved.append(url)
730
748
  continue
731
749
  if not res.ok:
732
750
  logger.info("self_report.link_unresolved url=%s tried=%s reason=%s",
733
751
  url, ",".join(res.attempts), res.reason)
734
752
  notes.append(f"Proof link {url} {res.reason}")
753
+ unresolved.append(url)
735
754
  continue
736
755
  body = res.text[:_RESOLVED_MAX_CHARS]
737
756
  truncated = "\n\n[truncated — the page was longer than this excerpt]" \
@@ -746,7 +765,7 @@ async def _resolve_link_proofs(
746
765
  files.append((f"link-{index}-{_link_slug(url)}.txt", content, "text/plain"))
747
766
  logger.info("self_report.link_resolved url=%s via=%s chars=%d",
748
767
  url, res.via, len(body))
749
- return files, notes
768
+ return files, notes, unresolved
750
769
 
751
770
 
752
771
  def _proof_record(p: Proof) -> dict[str, Any]:
@@ -1109,7 +1109,6 @@ class TestLinkProofsAreResolvedLocally:
1109
1109
  backend, _ = self._submit()
1110
1110
 
1111
1111
  sent = backend.submits[0]
1112
- assert sent["proof_urls"] == [self.URL], "the link a human would click was dropped"
1113
1112
  assert len(sent["files"]) == 1, "the page the SDK read was not attached"
1114
1113
  name, body, ctype = sent["files"][0]
1115
1114
  text = body.decode()
@@ -1120,6 +1119,29 @@ class TestLinkProofsAreResolvedLocally:
1120
1119
  assert f"Source: {self.URL}" in text
1121
1120
  assert "via: github-api" in text
1122
1121
 
1122
+ def test_a_resolved_link_is_NOT_also_sent_as_a_url(self, monkeypatch):
1123
+ """The regression: sending both made one link two contradictory proofs.
1124
+
1125
+ The backend fetches proof URLs server-side and anonymously. Left in the
1126
+ list, a link that WAS read here is fetched there, 404s, and lands beside
1127
+ its own successfully-read text as `unverifiable` — and the evaluator
1128
+ marks the criterion PARTIAL for the missing evidence, so a link that was
1129
+ read costs score for not having been read.
1130
+ """
1131
+ from sdk.url_access import Resolved
1132
+
1133
+ self._resolver(monkeypatch, Resolved(text="pull #118 … merged", via="github-api"))
1134
+ backend, _ = self._submit()
1135
+
1136
+ sent = backend.submits[0]
1137
+ assert sent["proof_urls"] == [], (
1138
+ "the resolved link was also sent as a URL — the backend will 404 on it "
1139
+ "and contradict the evidence the SDK just read")
1140
+ # …and the link is still recoverable, from the name and from the file.
1141
+ name, body, _ = sent["files"][0]
1142
+ assert self.URL.rsplit("/", 1)[-1] in name # …/pull/115 -> "…-115.txt"
1143
+ assert self.URL in body.decode()
1144
+
1123
1145
  def test_a_link_that_stays_shut_changes_nothing(self, monkeypatch):
1124
1146
  """The old behaviour is the floor: the backend still gets the URL."""
1125
1147
  from sdk.url_access import Resolved