vidbyte-cli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. vidbyte_cli/__init__.py +11 -0
  2. vidbyte_cli/__main__.py +6 -0
  3. vidbyte_cli/cli.py +18 -0
  4. vidbyte_cli/commands/__init__.py +86 -0
  5. vidbyte_cli/commands/agent_options.py +53 -0
  6. vidbyte_cli/commands/agents/__init__.py +31 -0
  7. vidbyte_cli/commands/agents/suggestion/__init__.py +38 -0
  8. vidbyte_cli/commands/agents/suggestion/prompts/__init__.py +3 -0
  9. vidbyte_cli/commands/agents/suggestion/prompts/all_categories.md +5 -0
  10. vidbyte_cli/commands/agents/suggestion/prompts/approaches.md +100 -0
  11. vidbyte_cli/commands/agents/suggestion/prompts/avoid.md +100 -0
  12. vidbyte_cli/commands/agents/suggestion/prompts/blockers.md +100 -0
  13. vidbyte_cli/commands/agents/suggestion/prompts/capability.md +100 -0
  14. vidbyte_cli/commands/agents/suggestion/prompts/categories.md +5 -0
  15. vidbyte_cli/commands/agents/suggestion/prompts/category.md +5 -0
  16. vidbyte_cli/commands/agents/suggestion/prompts/completed.md +100 -0
  17. vidbyte_cli/commands/agents/suggestion/prompts/constraint.md +100 -0
  18. vidbyte_cli/commands/agents/suggestion/prompts/context.md +100 -0
  19. vidbyte_cli/commands/agents/suggestion/prompts/count.md +5 -0
  20. vidbyte_cli/commands/agents/suggestion/prompts/critic_model.md +100 -0
  21. vidbyte_cli/commands/agents/suggestion/prompts/decision.md +100 -0
  22. vidbyte_cli/commands/agents/suggestion/prompts/dry_run.md +5 -0
  23. vidbyte_cli/commands/agents/suggestion/prompts/extra_compute.md +5 -0
  24. vidbyte_cli/commands/agents/suggestion/prompts/feedback_accept.md +7 -0
  25. vidbyte_cli/commands/agents/suggestion/prompts/feedback_group.md +7 -0
  26. vidbyte_cli/commands/agents/suggestion/prompts/feedback_project.md +100 -0
  27. vidbyte_cli/commands/agents/suggestion/prompts/feedback_reason.md +98 -0
  28. vidbyte_cli/commands/agents/suggestion/prompts/feedback_reject.md +7 -0
  29. vidbyte_cli/commands/agents/suggestion/prompts/feedback_suggestion.md +101 -0
  30. vidbyte_cli/commands/agents/suggestion/prompts/files.md +100 -0
  31. vidbyte_cli/commands/agents/suggestion/prompts/forbidden.md +100 -0
  32. vidbyte_cli/commands/agents/suggestion/prompts/goal.md +100 -0
  33. vidbyte_cli/commands/agents/suggestion/prompts/handoff.md +7 -0
  34. vidbyte_cli/commands/agents/suggestion/prompts/horizon.md +5 -0
  35. vidbyte_cli/commands/agents/suggestion/prompts/hypotheses.md +100 -0
  36. vidbyte_cli/commands/agents/suggestion/prompts/idea.md +100 -0
  37. vidbyte_cli/commands/agents/suggestion/prompts/in_progress.md +100 -0
  38. vidbyte_cli/commands/agents/suggestion/prompts/input.md +100 -0
  39. vidbyte_cli/commands/agents/suggestion/prompts/library.py +29 -0
  40. vidbyte_cli/commands/agents/suggestion/prompts/max_messages.md +5 -0
  41. vidbyte_cli/commands/agents/suggestion/prompts/max_output_tokens.md +5 -0
  42. vidbyte_cli/commands/agents/suggestion/prompts/max_total_tokens.md +5 -0
  43. vidbyte_cli/commands/agents/suggestion/prompts/mistakes.md +100 -0
  44. vidbyte_cli/commands/agents/suggestion/prompts/outcomes.md +100 -0
  45. vidbyte_cli/commands/agents/suggestion/prompts/project.md +100 -0
  46. vidbyte_cli/commands/agents/suggestion/prompts/project_create.md +7 -0
  47. vidbyte_cli/commands/agents/suggestion/prompts/project_description.md +98 -0
  48. vidbyte_cli/commands/agents/suggestion/prompts/project_group.md +7 -0
  49. vidbyte_cli/commands/agents/suggestion/prompts/project_key.md +96 -0
  50. vidbyte_cli/commands/agents/suggestion/prompts/project_list.md +7 -0
  51. vidbyte_cli/commands/agents/suggestion/prompts/project_title.md +99 -0
  52. vidbyte_cli/commands/agents/suggestion/prompts/provider.md +5 -0
  53. vidbyte_cli/commands/agents/suggestion/prompts/question.md +100 -0
  54. vidbyte_cli/commands/agents/suggestion/prompts/risks.md +100 -0
  55. vidbyte_cli/commands/agents/suggestion/prompts/rounds.md +5 -0
  56. vidbyte_cli/commands/agents/suggestion/prompts/run.md +5 -0
  57. vidbyte_cli/commands/agents/suggestion/prompts/success.md +100 -0
  58. vidbyte_cli/commands/agents/suggestion/prompts/timeout.md +5 -0
  59. vidbyte_cli/commands/agents/suggestion/prompts/trajectory.md +100 -0
  60. vidbyte_cli/commands/agents/suggestion/prompts/view.md +5 -0
  61. vidbyte_cli/commands/agents/suggestion/prompts/view_all.md +5 -0
  62. vidbyte_cli/commands/agents/suggestion/render.py +77 -0
  63. vidbyte_cli/commands/agents/suggestion/request_builder.py +240 -0
  64. vidbyte_cli/commands/agents/suggestion/suggest.py +154 -0
  65. vidbyte_cli/commands/agents/suggestion/suggestion_categories.py +77 -0
  66. vidbyte_cli/commands/agents/suggestion/suggestion_feedback.py +85 -0
  67. vidbyte_cli/commands/agents/suggestion/suggestion_handoff.py +67 -0
  68. vidbyte_cli/commands/agents/suggestion/suggestion_projects.py +95 -0
  69. vidbyte_cli/commands/auth/__init__.py +0 -0
  70. vidbyte_cli/commands/auth/login.py +100 -0
  71. vidbyte_cli/commands/auth/logout.py +41 -0
  72. vidbyte_cli/commands/auth/whoami.py +51 -0
  73. vidbyte_cli/commands/billing/__init__.py +20 -0
  74. vidbyte_cli/commands/billing/prompts/__init__.py +1 -0
  75. vidbyte_cli/commands/billing/prompts/confirm.md +16 -0
  76. vidbyte_cli/commands/billing/prompts/group.md +13 -0
  77. vidbyte_cli/commands/billing/prompts/idempotency_key.md +16 -0
  78. vidbyte_cli/commands/billing/prompts/library.py +18 -0
  79. vidbyte_cli/commands/billing/prompts/top_up.md +16 -0
  80. vidbyte_cli/commands/billing/top_up.py +65 -0
  81. vidbyte_cli/commands/config/__init__.py +0 -0
  82. vidbyte_cli/commands/config/get.py +45 -0
  83. vidbyte_cli/commands/config/set.py +39 -0
  84. vidbyte_cli/commands/provider/__init__.py +9 -0
  85. vidbyte_cli/commands/provider/login.py +102 -0
  86. vidbyte_cli/commands/provider/logout.py +45 -0
  87. vidbyte_cli/commands/provider/whoami.py +49 -0
  88. vidbyte_cli/commands/research/__init__.py +5 -0
  89. vidbyte_cli/commands/research/add.py +41 -0
  90. vidbyte_cli/commands/research/options.py +134 -0
  91. vidbyte_cli/commands/research/render.py +157 -0
  92. vidbyte_cli/commands/research/resume.py +37 -0
  93. vidbyte_cli/commands/research/start.py +37 -0
  94. vidbyte_cli/commands/research/status.py +32 -0
  95. vidbyte_cli/commands/research/thread.py +30 -0
  96. vidbyte_cli/commands/research/threads.py +35 -0
  97. vidbyte_cli/commands/research/watch.py +93 -0
  98. vidbyte_cli/commands/runtime/__init__.py +23 -0
  99. vidbyte_cli/commands/runtime/adversarial_team.py +41 -0
  100. vidbyte_cli/commands/runtime/doctor.py +44 -0
  101. vidbyte_cli/commands/runtime/list.py +47 -0
  102. vidbyte_cli/commands/runtime/persistence.py +141 -0
  103. vidbyte_cli/commands/runtime/same_host_ensemble.py +221 -0
  104. vidbyte_cli/commands/runtime/stages.py +553 -0
  105. vidbyte_cli/commands/runtime/task_board.py +1342 -0
  106. vidbyte_cli/commands/setup/__init__.py +0 -0
  107. vidbyte_cli/commands/setup/doctor.py +64 -0
  108. vidbyte_cli/commands/setup/prompts/__init__.py +1 -0
  109. vidbyte_cli/commands/setup/prompts/browser.md +7 -0
  110. vidbyte_cli/commands/setup/prompts/library.py +18 -0
  111. vidbyte_cli/commands/setup/prompts/star.md +13 -0
  112. vidbyte_cli/commands/setup/star.py +132 -0
  113. vidbyte_cli/lib/__init__.py +0 -0
  114. vidbyte_cli/lib/api/__init__.py +0 -0
  115. vidbyte_cli/lib/api/client.py +236 -0
  116. vidbyte_cli/lib/api/endpoints/__init__.py +0 -0
  117. vidbyte_cli/lib/api/endpoints/auth.py +24 -0
  118. vidbyte_cli/lib/api/endpoints/billing.py +36 -0
  119. vidbyte_cli/lib/api/endpoints/research.py +103 -0
  120. vidbyte_cli/lib/api/endpoints/runtime.py +113 -0
  121. vidbyte_cli/lib/api/problem.py +86 -0
  122. vidbyte_cli/lib/api/response.py +101 -0
  123. vidbyte_cli/lib/api/retry.py +116 -0
  124. vidbyte_cli/lib/api/runtime_payment.py +89 -0
  125. vidbyte_cli/lib/auth/__init__.py +16 -0
  126. vidbyte_cli/lib/auth/credentials.py +223 -0
  127. vidbyte_cli/lib/auth/input.py +59 -0
  128. vidbyte_cli/lib/auth/keyring_store.py +126 -0
  129. vidbyte_cli/lib/auth/provider_credentials.py +157 -0
  130. vidbyte_cli/lib/auth/provider_input.py +54 -0
  131. vidbyte_cli/lib/auth/provider_resolver.py +89 -0
  132. vidbyte_cli/lib/auth/provider_store.py +149 -0
  133. vidbyte_cli/lib/auth/provider_verifier.py +219 -0
  134. vidbyte_cli/lib/auth/resolver.py +63 -0
  135. vidbyte_cli/lib/auth/verifier.py +38 -0
  136. vidbyte_cli/lib/config/__init__.py +25 -0
  137. vidbyte_cli/lib/config/atomic.py +47 -0
  138. vidbyte_cli/lib/config/config.py +141 -0
  139. vidbyte_cli/lib/config/migration.py +81 -0
  140. vidbyte_cli/lib/config/models.py +145 -0
  141. vidbyte_cli/lib/config/paths.py +66 -0
  142. vidbyte_cli/lib/config/resolver.py +137 -0
  143. vidbyte_cli/lib/constants/__init__.py +1 -0
  144. vidbyte_cli/lib/constants/runtime.py +186 -0
  145. vidbyte_cli/lib/errors/__init__.py +12 -0
  146. vidbyte_cli/lib/errors/cli_error.py +86 -0
  147. vidbyte_cli/lib/errors/codes.py +43 -0
  148. vidbyte_cli/lib/errors/failures.py +2910 -0
  149. vidbyte_cli/lib/errors/handler.py +72 -0
  150. vidbyte_cli/lib/files/__init__.py +6 -0
  151. vidbyte_cli/lib/files/documents.py +94 -0
  152. vidbyte_cli/lib/files/store.py +156 -0
  153. vidbyte_cli/lib/io/__init__.py +16 -0
  154. vidbyte_cli/lib/io/attachments.py +129 -0
  155. vidbyte_cli/lib/io/codex_attachments.py +47 -0
  156. vidbyte_cli/lib/io/prompt.py +71 -0
  157. vidbyte_cli/lib/io/streams.py +37 -0
  158. vidbyte_cli/lib/io/terminal.py +66 -0
  159. vidbyte_cli/lib/output/__init__.py +7 -0
  160. vidbyte_cli/lib/output/formats.py +29 -0
  161. vidbyte_cli/lib/output/manager.py +95 -0
  162. vidbyte_cli/lib/output/models.py +49 -0
  163. vidbyte_cli/lib/runtime/__init__.py +9 -0
  164. vidbyte_cli/lib/runtime/application.py +134 -0
  165. vidbyte_cli/lib/runtime/context.py +250 -0
  166. vidbyte_cli/lib/runtime/options.py +159 -0
  167. vidbyte_cli/lib/runtime/version.py +30 -0
  168. vidbyte_cli/lib/runtime_primitives/__init__.py +28 -0
  169. vidbyte_cli/lib/runtime_primitives/executor.py +54 -0
  170. vidbyte_cli/lib/runtime_primitives/gate.py +105 -0
  171. vidbyte_cli/lib/runtime_primitives/hosts.py +45 -0
  172. vidbyte_cli/lib/runtime_primitives/planner.py +79 -0
  173. vidbyte_cli/lib/runtime_primitives/stages.py +338 -0
  174. vidbyte_cli/lib/runtime_primitives/task_board.py +982 -0
  175. vidbyte_cli/lib/runtime_primitives/task_board_checkpoints.py +473 -0
  176. vidbyte_cli/lib/runtime_primitives/task_board_system.md +33 -0
  177. vidbyte_cli/lib/runtime_primitives/task_board_tasks.py +111 -0
  178. vidbyte_cli/lib/runtime_primitives/verification.py +68 -0
  179. vidbyte_cli/services/__init__.py +5 -0
  180. vidbyte_cli/services/ensemble/__init__.py +7 -0
  181. vidbyte_cli/services/ensemble/prompts/__init__.py +6 -0
  182. vidbyte_cli/services/ensemble/prompts/implementer_system.md +546 -0
  183. vidbyte_cli/services/ensemble/prompts/implementer_turn.md +49 -0
  184. vidbyte_cli/services/ensemble/prompts/library.py +188 -0
  185. vidbyte_cli/services/ensemble/prompts/planner_system.md +728 -0
  186. vidbyte_cli/services/ensemble/prompts/planner_turn.md +6 -0
  187. vidbyte_cli/services/ensemble/prompts/role_system.md +104 -0
  188. vidbyte_cli/services/ensemble/prompts/role_turn.md +19 -0
  189. vidbyte_cli/services/ensemble/prompts/selector_final_turn.md +19 -0
  190. vidbyte_cli/services/ensemble/prompts/selector_round_turn.md +17 -0
  191. vidbyte_cli/services/ensemble/prompts/selector_system.md +89 -0
  192. vidbyte_cli/services/ensemble/runner.py +30 -0
  193. vidbyte_cli/services/ensemble/sdk.py +134 -0
  194. vidbyte_cli/services/ensemble/service.py +233 -0
  195. vidbyte_cli/services/ensemble/settings.py +60 -0
  196. vidbyte_cli/services/persistence/__init__.py +6 -0
  197. vidbyte_cli/services/persistence/prompts/__init__.py +6 -0
  198. vidbyte_cli/services/persistence/prompts/library.py +46 -0
  199. vidbyte_cli/services/persistence/prompts/persistence_system.md +1 -0
  200. vidbyte_cli/services/persistence/prompts/persistence_turn.md +3 -0
  201. vidbyte_cli/services/persistence/runner.py +35 -0
  202. vidbyte_cli/services/persistence/session.py +116 -0
  203. vidbyte_cli/services/suggestions/__init__.py +7 -0
  204. vidbyte_cli/services/suggestions/categories.py +270 -0
  205. vidbyte_cli/services/suggestions/context.py +222 -0
  206. vidbyte_cli/services/suggestions/extra_compute.py +53 -0
  207. vidbyte_cli/services/suggestions/handoff.py +173 -0
  208. vidbyte_cli/services/suggestions/message_tools.py +47 -0
  209. vidbyte_cli/services/suggestions/project.py +253 -0
  210. vidbyte_cli/services/suggestions/prompts/__init__.py +1 -0
  211. vidbyte_cli/services/suggestions/prompts/categories/__init__.py +3 -0
  212. vidbyte_cli/services/suggestions/prompts/categories/adjacent_opportunity.md +37 -0
  213. vidbyte_cli/services/suggestions/prompts/categories/alternative.md +38 -0
  214. vidbyte_cli/services/suggestions/prompts/categories/big_bets.md +37 -0
  215. vidbyte_cli/services/suggestions/prompts/categories/bottleneck.md +38 -0
  216. vidbyte_cli/services/suggestions/prompts/categories/business_growth.md +38 -0
  217. vidbyte_cli/services/suggestions/prompts/categories/completion.md +37 -0
  218. vidbyte_cli/services/suggestions/prompts/categories/continuation.md +37 -0
  219. vidbyte_cli/services/suggestions/prompts/categories/coordination.md +37 -0
  220. vidbyte_cli/services/suggestions/prompts/categories/creative_exploration.md +37 -0
  221. vidbyte_cli/services/suggestions/prompts/categories/cross_domain.md +37 -0
  222. vidbyte_cli/services/suggestions/prompts/categories/delegation.md +37 -0
  223. vidbyte_cli/services/suggestions/prompts/categories/experiment.md +37 -0
  224. vidbyte_cli/services/suggestions/prompts/categories/feedback.md +37 -0
  225. vidbyte_cli/services/suggestions/prompts/categories/goal_clarification.md +37 -0
  226. vidbyte_cli/services/suggestions/prompts/categories/immediate_next_steps.md +37 -0
  227. vidbyte_cli/services/suggestions/prompts/categories/investigation.md +37 -0
  228. vidbyte_cli/services/suggestions/prompts/categories/learning.md +37 -0
  229. vidbyte_cli/services/suggestions/prompts/categories/leverage.md +37 -0
  230. vidbyte_cli/services/suggestions/prompts/categories/long_term_directions.md +37 -0
  231. vidbyte_cli/services/suggestions/prompts/categories/preparation.md +37 -0
  232. vidbyte_cli/services/suggestions/prompts/categories/prerequisite.md +37 -0
  233. vidbyte_cli/services/suggestions/prompts/categories/prioritization.md +37 -0
  234. vidbyte_cli/services/suggestions/prompts/categories/product_experience.md +37 -0
  235. vidbyte_cli/services/suggestions/prompts/categories/quick_wins.md +37 -0
  236. vidbyte_cli/services/suggestions/prompts/categories/reframing.md +37 -0
  237. vidbyte_cli/services/suggestions/prompts/categories/risk_prevention.md +37 -0
  238. vidbyte_cli/services/suggestions/prompts/categories/simplification.md +37 -0
  239. vidbyte_cli/services/suggestions/prompts/categories/stop_or_defer.md +37 -0
  240. vidbyte_cli/services/suggestions/prompts/categories/strategy.md +37 -0
  241. vidbyte_cli/services/suggestions/prompts/categories/technical_possibilities.md +37 -0
  242. vidbyte_cli/services/suggestions/prompts/categories/verification.md +37 -0
  243. vidbyte_cli/services/suggestions/prompts/critic.md +209 -0
  244. vidbyte_cli/services/suggestions/prompts/critic_message.md +8 -0
  245. vidbyte_cli/services/suggestions/prompts/generator.md +64 -0
  246. vidbyte_cli/services/suggestions/prompts/library.py +72 -0
  247. vidbyte_cli/services/suggestions/prompts/message_generator_argument.md +1 -0
  248. vidbyte_cli/services/suggestions/prompts/message_generator_tool.md +1 -0
  249. vidbyte_cli/services/suggestions/prompts/message_parent_argument.md +1 -0
  250. vidbyte_cli/services/suggestions/prompts/message_parent_tool.md +1 -0
  251. vidbyte_cli/services/suggestions/prompts/message_receipt.md +1 -0
  252. vidbyte_cli/services/suggestions/prompts/revision.md +8 -0
  253. vidbyte_cli/services/suggestions/sdk.py +228 -0
  254. vidbyte_cli/services/suggestions/selection.py +78 -0
  255. vidbyte_cli/services/suggestions/service.py +540 -0
  256. vidbyte_cli/types/__init__.py +0 -0
  257. vidbyte_cli/types/api.py +101 -0
  258. vidbyte_cli/types/attachments.py +79 -0
  259. vidbyte_cli/types/ensemble.py +189 -0
  260. vidbyte_cli/types/provider.py +62 -0
  261. vidbyte_cli/types/research.py +260 -0
  262. vidbyte_cli/types/runtime.py +1098 -0
  263. vidbyte_cli/types/suggestions.py +518 -0
  264. vidbyte_cli-0.1.0.dist-info/METADATA +335 -0
  265. vidbyte_cli-0.1.0.dist-info/RECORD +269 -0
  266. vidbyte_cli-0.1.0.dist-info/WHEEL +5 -0
  267. vidbyte_cli-0.1.0.dist-info/entry_points.txt +2 -0
  268. vidbyte_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
  269. vidbyte_cli-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,228 @@
1
+ """Lazily binds the suggestion service to Vidbyte SDK Codex agents.
2
+
3
+ This is the only module that imports SDK symbols. It constructs read-only,
4
+ structured-output agents, places one validated stage context primitive in
5
+ each fresh context manager, and wraps each CLI message tool as an SDK tool.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections.abc import Mapping
11
+ from dataclasses import dataclass, field
12
+ from types import MappingProxyType
13
+ from typing import Any, Literal, Protocol, cast
14
+
15
+ from ...lib.errors.failures import SuggestionSdkUnavailable
16
+ from ...lib.io.codex_attachments import CodexAttachmentInputBuilder
17
+ from ...types.attachments import AttachmentBundle
18
+ from ...types.suggestions import SuggestionAgentContext
19
+ from .message_tools import SuggestionMessageTool
20
+
21
+ _CODEX_MODULE = "vidbyte.agents.codex"
22
+ _CONTEXT_MODULE = "vidbyte.context"
23
+ _ERRORS_MODULE = "vidbyte.lib.errors"
24
+ _TOOLS_MODULE = "vidbyte.tools"
25
+ _REQUIRED = (
26
+ "CodexAgentSettings",
27
+ "CodexApprovalMode",
28
+ "CodexHarnessAgentSettings",
29
+ "CodexRunInput",
30
+ "CodexSandbox",
31
+ "CodexThreadSettings",
32
+ "CodexTurnSettings",
33
+ )
34
+ _TOOL_SYMBOLS = ("BaseTool", "ToolParameter", "ToolResult", "ToolSpec")
35
+ _PROVIDER_MAP = {"openai": "openai"}
36
+
37
+
38
+ @dataclass(frozen=True, slots=True)
39
+ class SuggestionSdkBindings:
40
+ """Validated SDK classes and errors resolved together by the lazy loader."""
41
+
42
+ symbols: Mapping[str, Any]
43
+ agent_type: type
44
+ provider_error_type: type[Exception]
45
+ schema_error_type: type[Exception]
46
+
47
+ def __post_init__(self) -> None:
48
+ expected = frozenset((*_REQUIRED, *_TOOL_SYMBOLS, "ContextManager"))
49
+ if frozenset(self.symbols) != expected:
50
+ raise ValueError("Suggestion SDK bindings do not match the required surface.")
51
+ if any(not callable(symbol) for symbol in self.symbols.values()):
52
+ raise TypeError("Every suggestion SDK binding must be callable.")
53
+ if not isinstance(self.agent_type, type):
54
+ raise TypeError("Suggestion SDK agent_type must be a class.")
55
+ for field_name in ("provider_error_type", "schema_error_type"):
56
+ value = getattr(self, field_name)
57
+ if not isinstance(value, type) or not issubclass(value, Exception):
58
+ raise TypeError(f"Suggestion SDK {field_name} must be an Exception class.")
59
+ object.__setattr__(self, "symbols", MappingProxyType(dict(self.symbols)))
60
+
61
+
62
+ @dataclass(frozen=True, slots=True)
63
+ class SuggestionAgentSettingsInput:
64
+ """Strict local input for one generator or critic Codex configuration."""
65
+
66
+ role: Literal["generator", "critic"]
67
+ system_prompt: str
68
+ context: SuggestionAgentContext
69
+ output_schema: type | Mapping[str, Any]
70
+ provider: str | None = None
71
+ model: str | None = None
72
+ tools: tuple[SuggestionMessageTool, ...] = ()
73
+
74
+ def __post_init__(self) -> None:
75
+ if self.role not in ("generator", "critic"):
76
+ raise ValueError("Suggestion agent role must be generator or critic.")
77
+ if type(self.system_prompt) is not str or not self.system_prompt.strip():
78
+ raise ValueError("Suggestion agent system_prompt must be non-empty.")
79
+ if not isinstance(self.context, SuggestionAgentContext):
80
+ raise TypeError("Suggestion agent context must be SuggestionAgentContext.")
81
+ if not isinstance(self.output_schema, (type, Mapping)):
82
+ raise TypeError("Suggestion agent output_schema must be a class or mapping.")
83
+ if not isinstance(self.tools, tuple) or any(
84
+ not isinstance(tool, SuggestionMessageTool) for tool in self.tools
85
+ ):
86
+ raise TypeError("Suggestion agent tools must be a tuple of SuggestionMessageTool.")
87
+ for name, value in (("provider", self.provider), ("model", self.model)):
88
+ if value is not None and (type(value) is not str or not value.strip()):
89
+ raise ValueError(f"Suggestion agent {name} must be None or non-empty.")
90
+
91
+
92
+ @dataclass(frozen=True, slots=True)
93
+ class SuggestionTextInput:
94
+ """Strict local input for the single text turn used by this workflow."""
95
+
96
+ prompt: str
97
+ attachments: AttachmentBundle = field(default_factory=AttachmentBundle)
98
+
99
+ def __post_init__(self) -> None:
100
+ if type(self.prompt) is not str or not self.prompt.strip():
101
+ raise ValueError("Suggestion text prompt must be non-empty.")
102
+
103
+
104
+ class SuggestionAgent(Protocol):
105
+ """Subset of CodexHarnessAgent driven by the suggestion workflow."""
106
+
107
+ thread_id: str
108
+
109
+ async def arun(self, request: Any) -> Any: ...
110
+
111
+
112
+ class SuggestionSdk:
113
+ """Translates strict local values into fresh, read-only Codex agents."""
114
+
115
+ def __init__(self, bindings: SuggestionSdkBindings) -> None:
116
+ self._bindings = bindings
117
+
118
+ @classmethod
119
+ def load(cls) -> SuggestionSdk:
120
+ """Resolve the pinned SDK only when a model-backed operation needs it."""
121
+ try:
122
+ codex = __import__(_CODEX_MODULE, fromlist=["*"])
123
+ context = __import__(_CONTEXT_MODULE, fromlist=["ContextManager"])
124
+ errors = __import__(_ERRORS_MODULE, fromlist=["*"])
125
+ tools = __import__(_TOOLS_MODULE, fromlist=["*"])
126
+ symbols = {name: getattr(codex, name) for name in _REQUIRED}
127
+ symbols.update({name: getattr(tools, name) for name in _TOOL_SYMBOLS})
128
+ symbols["ContextManager"] = context.ContextManager
129
+ bindings = SuggestionSdkBindings(
130
+ symbols=symbols,
131
+ agent_type=codex.CodexHarnessAgent,
132
+ provider_error_type=errors.CodexAgentError,
133
+ schema_error_type=errors.OutputSchemaViolationError,
134
+ )
135
+ except (ImportError, AttributeError, TypeError, ValueError) as error:
136
+ raise SuggestionSdkUnavailable(error) from error
137
+ return cls(bindings)
138
+
139
+ def agent(self, settings: Any) -> SuggestionAgent:
140
+ """Construct one new SDK agent from already validated settings."""
141
+ return cast(SuggestionAgent, self._bindings.agent_type(settings))
142
+
143
+ def run_input(self, request: SuggestionTextInput) -> Any:
144
+ """Translate one local text request into the SDK's typed input."""
145
+ if not isinstance(request, SuggestionTextInput):
146
+ raise TypeError("run_input requires SuggestionTextInput.")
147
+ return CodexAttachmentInputBuilder().build(request.prompt, request.attachments)
148
+
149
+ def is_provider_error(self, error: Exception) -> bool:
150
+ """Identify SDK transport/host failures without importing SDK types elsewhere."""
151
+ return isinstance(error, self._bindings.provider_error_type)
152
+
153
+ def is_schema_error(self, error: Exception) -> bool:
154
+ """Identify a provider reply that violated the declared output schema."""
155
+ return isinstance(error, self._bindings.schema_error_type)
156
+
157
+ def agent_settings(self, request: SuggestionAgentSettingsInput) -> Any:
158
+ """Build the exact read-only SDK settings for one independent context window."""
159
+ if not isinstance(request, SuggestionAgentSettingsInput):
160
+ raise TypeError("agent_settings requires SuggestionAgentSettingsInput.")
161
+ symbols = self._bindings.symbols
162
+ provider = "" if request.provider is None else _PROVIDER_MAP.get(request.provider)
163
+ if request.provider is not None and provider is None:
164
+ raise ValueError(f"Unsupported suggestion provider: {request.provider}")
165
+ sandbox = symbols["CodexSandbox"].READ_ONLY
166
+ deny = symbols["CodexApprovalMode"].DENY_ALL
167
+ codex = symbols["CodexAgentSettings"](
168
+ thread=symbols["CodexThreadSettings"](
169
+ model=request.model or "",
170
+ model_provider=provider or "",
171
+ sandbox=sandbox,
172
+ approval_mode=deny,
173
+ ),
174
+ turn=symbols["CodexTurnSettings"](
175
+ model=request.model or "",
176
+ sandbox=sandbox,
177
+ approval_mode=deny,
178
+ ),
179
+ )
180
+ manager = symbols["ContextManager"]()
181
+ manager.place_after_system_prompt(request.context)
182
+ return symbols["CodexHarnessAgentSettings"](
183
+ name=f"suggestion-{request.role}",
184
+ system_prompt=request.system_prompt,
185
+ codex=codex,
186
+ context_manager=manager,
187
+ output_schema=request.output_schema,
188
+ tools=tuple(self._message_tool(tool) for tool in request.tools),
189
+ )
190
+
191
+ def _message_tool(self, tool: SuggestionMessageTool) -> Any:
192
+ # Codex runs custom tools as SDK BaseTools, so each CLI tool gets a thin BaseTool
193
+ # whose spec is its authored text and whose execute delivers the model's message.
194
+ symbols = self._bindings.symbols
195
+ spec = symbols["ToolSpec"](
196
+ name=tool.name,
197
+ description=tool.description,
198
+ parameters=(
199
+ symbols["ToolParameter"](
200
+ name="message", type="string", description=tool.message_description
201
+ ),
202
+ ),
203
+ )
204
+ result = symbols["ToolResult"]
205
+ base: Any = symbols["BaseTool"]
206
+
207
+ # The base class is only known after the lazy SDK import, so mypy sees it as Any.
208
+ class _MessageTool(base): # type: ignore[misc]
209
+ def spec(self) -> Any:
210
+ return spec
211
+
212
+ async def execute(self, call: Any) -> Any:
213
+ # An invalid message is returned to the model so it can retry the call.
214
+ try:
215
+ return result.success(tool.name, tool.deliver(call.arguments.get("message")))
216
+ except ValueError as error:
217
+ return result.error(tool.name, str(error))
218
+
219
+ return _MessageTool()
220
+
221
+
222
+ __all__ = [
223
+ "SuggestionAgent",
224
+ "SuggestionAgentSettingsInput",
225
+ "SuggestionSdk",
226
+ "SuggestionSdkBindings",
227
+ "SuggestionTextInput",
228
+ ]
@@ -0,0 +1,78 @@
1
+ """Validates eligibility, ranking, and redundancy for candidate ideas.
2
+
3
+ Exact duplicates are removed in code by normalized title; semantic overlap is
4
+ left to the generator's revision, guided by the critic's review. Selection
5
+ never invents evidence: every evidence ref must already exist in the run's
6
+ context manifest.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from ...types.suggestions import SuggestionIdea
12
+
13
+
14
+ class SuggestionSelection:
15
+ """Applies deterministic selection rules over reviewed candidates."""
16
+
17
+ def __init__(self) -> None:
18
+ # Stateless across runs; all inputs arrive per call.
19
+ pass
20
+
21
+ def validate_evidence(
22
+ self, ideas: tuple[SuggestionIdea, ...], allowed: set[str]
23
+ ) -> tuple[SuggestionIdea, ...]:
24
+ # Drops ideas that cite refs the manifest never issued.
25
+ return tuple(idea for idea in ideas if set(idea.evidence_refs) <= allowed)
26
+
27
+ def validate_categories(
28
+ self, ideas: tuple[SuggestionIdea, ...], allowed: set[str]
29
+ ) -> tuple[SuggestionIdea, ...]:
30
+ # Category ids are checked after model output so a provider cannot invent a lens.
31
+ return tuple(
32
+ idea
33
+ for idea in ideas
34
+ if idea.primary_category in allowed and set(idea.secondary_categories) <= allowed
35
+ )
36
+
37
+ def deduplicate(self, ideas: tuple[SuggestionIdea, ...]) -> tuple[SuggestionIdea, ...]:
38
+ # Keeps the first idea per normalized title so ordering stays stable.
39
+ seen: set[str] = set()
40
+ kept: list[SuggestionIdea] = []
41
+ for idea in ideas:
42
+ key = " ".join(idea.title.lower().split())
43
+ if key in seen:
44
+ continue
45
+ seen.add(key)
46
+ kept.append(idea)
47
+ return tuple(kept)
48
+
49
+ def suppress_rejected(
50
+ self, ideas: tuple[SuggestionIdea, ...], rejected: tuple[str, ...]
51
+ ) -> tuple[SuggestionIdea, ...]:
52
+ # Removes ideas whose title or summary echoes caller-rejected directions.
53
+ needles = [item.lower() for item in rejected if item.strip()]
54
+ if not needles:
55
+ return ideas
56
+ kept: list[SuggestionIdea] = []
57
+ for idea in ideas:
58
+ haystack = (
59
+ f"{idea.title} {idea.summary} {idea.first_action} "
60
+ f"{' '.join(idea.suggested_actions)}"
61
+ ).lower()
62
+ if any(needle and needle in haystack for needle in needles):
63
+ continue
64
+ kept.append(idea)
65
+ return tuple(kept)
66
+
67
+ def rank(self, ideas: tuple[SuggestionIdea, ...], limit: int) -> tuple[SuggestionIdea, ...]:
68
+ # Reassigns rank sequentially after trimming to the requested maximum.
69
+ return tuple(
70
+ idea.model_copy(update={"rank": index}) for index, idea in enumerate(ideas[:limit], 1)
71
+ )
72
+
73
+ def coverage(self, ideas: tuple[SuggestionIdea, ...]) -> dict[str, int]:
74
+ # Counts primary categories represented in the final slate.
75
+ counts: dict[str, int] = {}
76
+ for idea in ideas:
77
+ counts[idea.primary_category] = counts.get(idea.primary_category, 0) + 1
78
+ return counts