py-harness-cli 0.3.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 (92) hide show
  1. finetune/__init__.py +1 -0
  2. finetune/agent_system.py +41 -0
  3. finetune/agent_traces.py +157 -0
  4. finetune/everyday.py +30 -0
  5. finetune/hf_ollama.py +158 -0
  6. finetune/huggingface_store.py +144 -0
  7. finetune/models.py +74 -0
  8. finetune/paths.py +11 -0
  9. finetune/python_vibe.py +788 -0
  10. finetune/splits.py +54 -0
  11. finetune/systems.py +9 -0
  12. harness/__init__.py +42 -0
  13. harness/__main__.py +8 -0
  14. harness/act/__init__.py +6 -0
  15. harness/act/autofix/__init__.py +110 -0
  16. harness/act/autofix/additions.py +217 -0
  17. harness/act/autofix/conflicts.py +124 -0
  18. harness/act/autofix/cover.py +419 -0
  19. harness/act/autofix/mechanical.py +151 -0
  20. harness/act/autofix/missing_imports.py +50 -0
  21. harness/act/autofix/moves.py +439 -0
  22. harness/act/autofix/names.py +339 -0
  23. harness/act/autofix/scaffold.py +224 -0
  24. harness/act/code.py +157 -0
  25. harness/act/gate.py +229 -0
  26. harness/act/parse.py +247 -0
  27. harness/act/patch_fix.py +138 -0
  28. harness/act/tools.py +244 -0
  29. harness/agent/__init__.py +11 -0
  30. harness/agent/dispatch.py +235 -0
  31. harness/agent/loop.py +699 -0
  32. harness/agent/options.py +144 -0
  33. harness/agent/policy.py +856 -0
  34. harness/agent/prompt.py +170 -0
  35. harness/cli.py +393 -0
  36. harness/editor_kit.py +265 -0
  37. harness/guard/__init__.py +6 -0
  38. harness/guard/fallbacks.py +6 -0
  39. harness/guard/loop_guard.py +57 -0
  40. harness/guard/python_vibe.py +68 -0
  41. harness/guard/run.py +41 -0
  42. harness/guard/types.py +19 -0
  43. harness/locate.py +767 -0
  44. harness/mcp_stdio.py +306 -0
  45. harness/memory/__init__.py +5 -0
  46. harness/memory/conversation.py +104 -0
  47. harness/model/__init__.py +6 -0
  48. harness/model/chat_backend.py +100 -0
  49. harness/model/engine.py +165 -0
  50. harness/model/ollama_generate.py +60 -0
  51. harness/model/openai_generate.py +156 -0
  52. harness/model/outbound.py +83 -0
  53. harness/model/route.py +90 -0
  54. harness/observe/__init__.py +6 -0
  55. harness/observe/eval_gate.py +80 -0
  56. harness/observe/eval_loop.py +185 -0
  57. harness/observe/eval_tasks.py +399 -0
  58. harness/observe/report_md.py +102 -0
  59. harness/observe/trace_record.py +79 -0
  60. harness/openai_api.py +81 -0
  61. harness/paths.py +88 -0
  62. harness/py.typed +0 -0
  63. harness/scan/__init__.py +6 -0
  64. harness/scan/app_spec.py +338 -0
  65. harness/scan/design.py +112 -0
  66. harness/scan/existing.py +131 -0
  67. harness/scan/layout.py +254 -0
  68. harness/scan/names.py +308 -0
  69. harness/scan/project_brief.py +287 -0
  70. harness/scan/project_docs.py +42 -0
  71. harness/scan/project_scan.py +49 -0
  72. harness/scan/repo_map.py +101 -0
  73. harness/secrets.py +39 -0
  74. harness/server.py +199 -0
  75. harness/ship/__init__.py +1 -0
  76. harness/ship/bot_pr.py +221 -0
  77. harness/ship/git_ship.py +262 -0
  78. harness/ship/identity.py +62 -0
  79. harness/ship/ticket.py +251 -0
  80. harness/skillkit/__init__.py +6 -0
  81. harness/skillkit/catalog.py +241 -0
  82. harness/skillkit/refuse_change.py +640 -0
  83. harness/skillkit/refuse_finish.py +295 -0
  84. harness/skillkit/target.py +238 -0
  85. harness/task.py +717 -0
  86. py_harness_cli-0.3.0.dist-info/METADATA +177 -0
  87. py_harness_cli-0.3.0.dist-info/RECORD +92 -0
  88. py_harness_cli-0.3.0.dist-info/WHEEL +5 -0
  89. py_harness_cli-0.3.0.dist-info/entry_points.txt +3 -0
  90. py_harness_cli-0.3.0.dist-info/licenses/LICENSE +202 -0
  91. py_harness_cli-0.3.0.dist-info/licenses/NOTICE +6 -0
  92. py_harness_cli-0.3.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,144 @@
1
+ """The inputs and outputs of a run.
2
+
3
+ `AgentOptions` is everything the caller chooses. `AgentResult` is
4
+ everything the run reports back. These two classes are the public interface
5
+ of the harness; the other modules in this package are how the run is
6
+ carried out.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from collections.abc import Callable
12
+ from dataclasses import dataclass, field
13
+ from pathlib import Path
14
+
15
+ # The everyday model name and system prompt are product defaults that the
16
+ # training side also needs, so they are defined there and imported here.
17
+ # This is the only place `harness` reaches into `finetune`.
18
+ from finetune.agent_system import AGENT_SYSTEM
19
+ from finetune.everyday import DEFAULT_EVERYDAY_OLLAMA
20
+
21
+ DEFAULT_STEPS = 20
22
+ DEFAULT_MAX_TOKENS = 700
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class AgentOptions:
27
+ """Settings for one run.
28
+
29
+ Fields:
30
+ project: directory the agent may read and write inside.
31
+ task: what the user asked for, in their own words.
32
+ model: name of the Ollama model to use.
33
+ engine: "ollama", "mlx", or "openai" (remote OpenAI-compatible HTTP).
34
+ scope: subdirectory to stay within. Empty means the whole project.
35
+ skills: skill names to load. Empty means choose them from the task.
36
+ steps: maximum number of model turns before the run stops.
37
+ max_tokens: maximum length of one model reply.
38
+ allow_writes: when False, patch, edit and run are refused and the
39
+ project is not modified. Used for the HTTP server and --dry-run.
40
+ record: file to append redacted turns to, for training data.
41
+ None means the project's own `.python-vibe/traces.jsonl`,
42
+ which is the default: a run that records nothing leaves no
43
+ way to measure it later, and every trace thrown away is a
44
+ trace nobody gets back. `keep_no_record` turns it off.
45
+ keep_no_record: write no trace at all.
46
+ system: system prompt template. Placeholders are filled per run.
47
+ on_event: called with progress messages. None means print nothing.
48
+ on_question: called when the agent asks the user something. None
49
+ means nobody is available to answer, and the run stops instead.
50
+ """
51
+
52
+ project: Path
53
+ task: str = ""
54
+ model: str = DEFAULT_EVERYDAY_OLLAMA
55
+ engine: str = "ollama"
56
+ scope: str = ""
57
+ skills: tuple[str, ...] = ()
58
+ steps: int = DEFAULT_STEPS
59
+ max_tokens: int = DEFAULT_MAX_TOKENS
60
+ allow_writes: bool = True
61
+ record: Path | None = None
62
+ keep_no_record: bool = False
63
+ system: str = AGENT_SYSTEM
64
+ on_event: Callable[[str, str], None] | None = None
65
+ # Answering a question is optional. No handler means the loop stops
66
+ # and hands the question back rather than guessing silently.
67
+ on_question: Callable[..., str] | None = None
68
+
69
+ def resolved_project(self) -> Path:
70
+ project = self.project.expanduser().resolve()
71
+ if not project.is_dir():
72
+ raise ValueError(f"not a directory: {project}")
73
+ return project
74
+
75
+ def emit(self, kind: str, text: str) -> None:
76
+ if self.on_event is not None:
77
+ self.on_event(kind, text)
78
+
79
+
80
+ @dataclass(frozen=True)
81
+ class Step:
82
+ """One turn of the loop.
83
+
84
+ Fields:
85
+ number: position in the run, starting at 1.
86
+ action: the action the model asked for, or "" if it could not be read.
87
+ path: file the action applied to.
88
+ result: text returned to the model.
89
+ refused: reason the action was not carried out, or "" if it ran.
90
+ draft: the model's full reply for this turn.
91
+ """
92
+
93
+ number: int
94
+ action: str
95
+ path: str = ""
96
+ result: str = ""
97
+ refused: str = ""
98
+ draft: str = ""
99
+
100
+ @property
101
+ def ran(self) -> bool:
102
+ return not self.refused
103
+
104
+
105
+ @dataclass(frozen=True)
106
+ class AgentResult:
107
+ """What happened during a run.
108
+
109
+ Fields:
110
+ ok: True when the agent finished the task.
111
+ summary: the agent's closing sentence, or the reason it stopped.
112
+ stopped: "done", "steps" when the step budget ran out, or
113
+ "question" when the agent needs an answer to continue.
114
+ steps: every turn, in order.
115
+ writes: files that were changed.
116
+ """
117
+
118
+ ok: bool
119
+ summary: str
120
+ stopped: str
121
+ steps: tuple[Step, ...] = ()
122
+ writes: tuple[str, ...] = field(default_factory=tuple)
123
+
124
+ @property
125
+ def refusals(self) -> tuple[str, ...]:
126
+ return tuple(step.refused for step in self.steps if step.refused)
127
+
128
+ def as_dict(self) -> dict:
129
+ return {
130
+ "ok": self.ok,
131
+ "summary": self.summary,
132
+ "stopped": self.stopped,
133
+ "steps": [
134
+ {
135
+ "number": step.number,
136
+ "action": step.action,
137
+ "path": step.path,
138
+ "refused": step.refused,
139
+ "result": step.result[:2000],
140
+ }
141
+ for step in self.steps
142
+ ],
143
+ "writes": list(self.writes),
144
+ }