nexus-coding-agent 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 (131) hide show
  1. nexus/__init__.py +4 -0
  2. nexus/application/__init__.py +2 -0
  3. nexus/application/approval_service.py +130 -0
  4. nexus/application/diff_service.py +177 -0
  5. nexus/application/event_publisher.py +180 -0
  6. nexus/application/execution_context.py +29 -0
  7. nexus/application/execution_ledger.py +126 -0
  8. nexus/application/model_call_context.py +29 -0
  9. nexus/application/observed_model_gateway.py +194 -0
  10. nexus/application/plan_approval_service.py +250 -0
  11. nexus/application/planning.py +668 -0
  12. nexus/application/runtime.py +624 -0
  13. nexus/application/session_service.py +411 -0
  14. nexus/application/structured_output.py +96 -0
  15. nexus/application/telemetry.py +396 -0
  16. nexus/application/telemetry_subscriber.py +216 -0
  17. nexus/application/tool_runtime.py +496 -0
  18. nexus/application/tracing_dispatcher.py +283 -0
  19. nexus/application/validation.py +293 -0
  20. nexus/config/__init__.py +17 -0
  21. nexus/config/loader.py +253 -0
  22. nexus/config/models.py +310 -0
  23. nexus/context/__init__.py +6 -0
  24. nexus/context/budget.py +24 -0
  25. nexus/context/builder.py +92 -0
  26. nexus/context/chunking.py +65 -0
  27. nexus/context/explorer.py +246 -0
  28. nexus/context/integration.py +63 -0
  29. nexus/context/manager.py +328 -0
  30. nexus/context/retrieval.py +261 -0
  31. nexus/domain/__init__.py +2 -0
  32. nexus/domain/agent_decision.py +113 -0
  33. nexus/domain/agent_state.py +49 -0
  34. nexus/domain/approvals.py +64 -0
  35. nexus/domain/context.py +108 -0
  36. nexus/domain/exploration.py +86 -0
  37. nexus/domain/mcp.py +51 -0
  38. nexus/domain/model.py +173 -0
  39. nexus/domain/observability.py +326 -0
  40. nexus/domain/persistence.py +122 -0
  41. nexus/domain/planning.py +329 -0
  42. nexus/domain/ports/__init__.py +61 -0
  43. nexus/domain/ports/agent_decision.py +9 -0
  44. nexus/domain/ports/approval_unit_of_work.py +27 -0
  45. nexus/domain/ports/checkpoint_provider.py +11 -0
  46. nexus/domain/ports/context.py +71 -0
  47. nexus/domain/ports/events.py +21 -0
  48. nexus/domain/ports/graph_runtime.py +30 -0
  49. nexus/domain/ports/mcp.py +23 -0
  50. nexus/domain/ports/model_gateway.py +21 -0
  51. nexus/domain/ports/model_input_budget.py +15 -0
  52. nexus/domain/ports/planning.py +62 -0
  53. nexus/domain/ports/repositories.py +54 -0
  54. nexus/domain/ports/repository_context.py +18 -0
  55. nexus/domain/ports/session_unit_of_work.py +33 -0
  56. nexus/domain/ports/skills.py +41 -0
  57. nexus/domain/ports/tooling.py +39 -0
  58. nexus/domain/ports/tracer.py +13 -0
  59. nexus/domain/ports/validation.py +32 -0
  60. nexus/domain/runtime_events.py +445 -0
  61. nexus/domain/skills.py +129 -0
  62. nexus/domain/tooling.py +163 -0
  63. nexus/domain/validation.py +161 -0
  64. nexus/errors/__init__.py +82 -0
  65. nexus/evaluation/__init__.py +7 -0
  66. nexus/evaluation/assertions.py +4 -0
  67. nexus/evaluation/evaluator.py +263 -0
  68. nexus/evaluation/fixtures.py +36 -0
  69. nexus/evaluation/harness.py +606 -0
  70. nexus/evaluation/loader.py +415 -0
  71. nexus/evaluation/metrics.py +27 -0
  72. nexus/evaluation/models.py +362 -0
  73. nexus/evaluation/report.py +43 -0
  74. nexus/evaluation/runner.py +480 -0
  75. nexus/infrastructure/__init__.py +2 -0
  76. nexus/infrastructure/asyncio_compat.py +13 -0
  77. nexus/infrastructure/bootstrap/__init__.py +17 -0
  78. nexus/infrastructure/bootstrap/composition.py +663 -0
  79. nexus/infrastructure/checkpoint/__init__.py +5 -0
  80. nexus/infrastructure/checkpoint/postgres.py +62 -0
  81. nexus/infrastructure/database.py +39 -0
  82. nexus/infrastructure/embedding.py +61 -0
  83. nexus/infrastructure/graph/__init__.py +6 -0
  84. nexus/infrastructure/graph/day4_runtime.py +966 -0
  85. nexus/infrastructure/graph/langgraph_runtime.py +172 -0
  86. nexus/infrastructure/mcp/__init__.py +5 -0
  87. nexus/infrastructure/mcp/manager.py +380 -0
  88. nexus/infrastructure/model_gateway/__init__.py +6 -0
  89. nexus/infrastructure/model_gateway/openai_compatible.py +345 -0
  90. nexus/infrastructure/observability/__init__.py +7 -0
  91. nexus/infrastructure/observability/console.py +31 -0
  92. nexus/infrastructure/observability/langsmith.py +236 -0
  93. nexus/infrastructure/observability/structured_logger.py +70 -0
  94. nexus/infrastructure/persistence/__init__.py +13 -0
  95. nexus/infrastructure/persistence/approval_unit_of_work.py +74 -0
  96. nexus/infrastructure/persistence/models.py +146 -0
  97. nexus/infrastructure/persistence/repositories.py +326 -0
  98. nexus/infrastructure/persistence/unit_of_work.py +82 -0
  99. nexus/infrastructure/repository_files.py +117 -0
  100. nexus/infrastructure/sandbox/__init__.py +5 -0
  101. nexus/infrastructure/sandbox/local_process.py +353 -0
  102. nexus/infrastructure/semantic.py +276 -0
  103. nexus/infrastructure/semantic_models.py +56 -0
  104. nexus/interfaces/__init__.py +2 -0
  105. nexus/interfaces/cli/__init__.py +2 -0
  106. nexus/interfaces/cli/__main__.py +6 -0
  107. nexus/interfaces/cli/app.py +266 -0
  108. nexus/interfaces/cli/renderer.py +118 -0
  109. nexus/security/__init__.py +14 -0
  110. nexus/security/approval_policies.py +73 -0
  111. nexus/security/command_policy.py +117 -0
  112. nexus/security/executables.py +107 -0
  113. nexus/security/git_commands.py +65 -0
  114. nexus/security/workspace.py +93 -0
  115. nexus/skills/__init__.py +7 -0
  116. nexus/skills/builtin/__init__.py +1 -0
  117. nexus/skills/builtin/debug-python/SKILL.md +26 -0
  118. nexus/skills/builtin/review-repository/SKILL.md +26 -0
  119. nexus/skills/builtin/write-tests/SKILL.md +26 -0
  120. nexus/skills/loader.py +439 -0
  121. nexus/skills/registry.py +146 -0
  122. nexus/skills/selector.py +152 -0
  123. nexus/tools/__init__.py +6 -0
  124. nexus/tools/editing.py +312 -0
  125. nexus/tools/mcp.py +160 -0
  126. nexus/tools/native.py +654 -0
  127. nexus/tools/registry.py +27 -0
  128. nexus_coding_agent-0.1.0.dist-info/METADATA +142 -0
  129. nexus_coding_agent-0.1.0.dist-info/RECORD +131 -0
  130. nexus_coding_agent-0.1.0.dist-info/WHEEL +4 -0
  131. nexus_coding_agent-0.1.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,362 @@
1
+ """Frozen Day 9 evaluation contracts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+ from dataclasses import dataclass
7
+ from datetime import datetime
8
+ from enum import StrEnum
9
+ from pathlib import Path
10
+ from types import MappingProxyType
11
+ from typing import Protocol
12
+
13
+ from nexus.domain.model import TokenUsage
14
+ from nexus.domain.observability import TraceRunFinish
15
+ from nexus.domain.planning import TerminalStatus
16
+ from nexus.domain.runtime_events import RuntimeStatus
17
+ from nexus.domain.tooling import JsonObject
18
+
19
+
20
+ @dataclass(frozen=True, slots=True)
21
+ class RequiredFact:
22
+ fact_id: str
23
+ description: str
24
+ required_terms: tuple[str, ...]
25
+
26
+
27
+ class EvalAssertionType(StrEnum):
28
+ VALIDATION_PASSES = "validation_passes"
29
+ NO_UNAUTHORIZED_CHANGES = "no_unauthorized_changes"
30
+ NO_FORBIDDEN_CHANGES = "no_forbidden_changes"
31
+ NO_REPOSITORY_CHANGES = "no_repository_changes"
32
+ REQUIRED_PATH_CHANGED = "required_path_changed"
33
+ REQUIRED_PATH_CREATED = "required_path_created"
34
+ REQUIRED_TEXT_IN_FILE = "required_text_in_file"
35
+ REQUIRED_FINAL_FACTS = "required_final_facts"
36
+ REQUIRED_SECURITY_EVIDENCE = "required_security_evidence"
37
+ TARGETED_TEST_ADDED = "targeted_test_added"
38
+
39
+
40
+ @dataclass(frozen=True, slots=True)
41
+ class EvalAssertion:
42
+ assertion_type: EvalAssertionType
43
+ path: str | None = None
44
+ expected_text: str | None = None
45
+ required_facts: tuple[RequiredFact, ...] = ()
46
+ security_codes: tuple[str, ...] = ()
47
+ expected_test_node_ids: tuple[str, ...] = ()
48
+ metadata: JsonObject | None = None
49
+
50
+
51
+ @dataclass(frozen=True, slots=True)
52
+ class EvalCase:
53
+ case_id: str
54
+ initial_repo_state: str
55
+ task: str
56
+ allowed_files: tuple[str, ...]
57
+ forbidden_files: tuple[str, ...]
58
+ pre_validation_command: tuple[str, ...]
59
+ validation_command: tuple[str, ...]
60
+ validation_timeout_seconds: int | None
61
+ test_discovery_command: tuple[str, ...]
62
+ expected_behavior: str
63
+ deterministic_success_conditions: tuple[EvalAssertion, ...]
64
+ max_reasonable_steps: int
65
+ metadata: JsonObject | None = None
66
+
67
+
68
+ @dataclass(frozen=True, slots=True)
69
+ class FileFingerprint:
70
+ path: str
71
+ kind: str
72
+ content_sha256: str | None
73
+ executable: bool
74
+ symlink_target: str | None
75
+
76
+
77
+ @dataclass(frozen=True, slots=True)
78
+ class ForbiddenPathSnapshot:
79
+ path: str
80
+ exists: bool
81
+ kind: str | None
82
+ content_sha256: str | None
83
+ executable: bool | None
84
+ symlink_target: str | None
85
+
86
+
87
+ @dataclass(frozen=True, slots=True)
88
+ class RepositoryState:
89
+ files: Mapping[str, FileFingerprint]
90
+ forbidden_snapshots: Mapping[str, ForbiddenPathSnapshot]
91
+
92
+ def __post_init__(self) -> None:
93
+ object.__setattr__(self, "files", MappingProxyType(dict(self.files)))
94
+ object.__setattr__(
95
+ self,
96
+ "forbidden_snapshots",
97
+ MappingProxyType(dict(self.forbidden_snapshots)),
98
+ )
99
+
100
+
101
+ @dataclass(frozen=True, slots=True)
102
+ class EvalForbiddenPathEvidence:
103
+ path: str
104
+ before: ForbiddenPathSnapshot
105
+ after: ForbiddenPathSnapshot
106
+ changed: bool
107
+
108
+
109
+ @dataclass(frozen=True, slots=True)
110
+ class EvalRepositoryEvidence:
111
+ changed_files: tuple[str, ...]
112
+ created_files: tuple[str, ...]
113
+ deleted_files: tuple[str, ...]
114
+ forbidden_paths: tuple[EvalForbiddenPathEvidence, ...]
115
+ final_text_files: Mapping[str, str]
116
+
117
+ def __post_init__(self) -> None:
118
+ object.__setattr__(self, "changed_files", tuple(self.changed_files))
119
+ object.__setattr__(self, "created_files", tuple(self.created_files))
120
+ object.__setattr__(self, "deleted_files", tuple(self.deleted_files))
121
+ object.__setattr__(self, "forbidden_paths", tuple(self.forbidden_paths))
122
+ object.__setattr__(self, "final_text_files", MappingProxyType(dict(self.final_text_files)))
123
+
124
+
125
+ class EvalHarnessOperation(StrEnum):
126
+ GIT_INIT = "GIT_INIT"
127
+ GIT_ADD_BASELINE = "GIT_ADD_BASELINE"
128
+ GIT_COMMIT_BASELINE = "GIT_COMMIT_BASELINE"
129
+ GIT_STATUS = "GIT_STATUS"
130
+ GIT_DIFF = "GIT_DIFF"
131
+ GIT_LS_FILES = "GIT_LS_FILES"
132
+ VALIDATION = "VALIDATION"
133
+ TEST_DISCOVERY = "TEST_DISCOVERY"
134
+
135
+
136
+ @dataclass(frozen=True, slots=True)
137
+ class EvalHarnessCommand:
138
+ operation: EvalHarnessOperation
139
+ argv: tuple[str, ...]
140
+ cwd: Path
141
+ timeout_seconds: int
142
+
143
+
144
+ @dataclass(frozen=True, slots=True)
145
+ class EvalHarnessCommandResult:
146
+ operation: EvalHarnessOperation
147
+ allowed: bool
148
+ denial_code: str | None
149
+ exit_code: int | None
150
+ stdout_summary: str
151
+ stderr_summary: str
152
+ duration_ms: int
153
+ timed_out: bool
154
+ infrastructure_error: str | None
155
+
156
+
157
+ class SecurityEvidenceSource(StrEnum):
158
+ COMMAND_POLICY = "COMMAND_POLICY"
159
+ APPROVAL_POLICY = "APPROVAL_POLICY"
160
+ TOOL_RUNTIME = "TOOL_RUNTIME"
161
+ WORKSPACE_GUARD = "WORKSPACE_GUARD"
162
+ MCP_POLICY = "MCP_POLICY"
163
+ RUNTIME_EVENT = "RUNTIME_EVENT"
164
+
165
+
166
+ class SecurityDecision(StrEnum):
167
+ DENIED = "DENIED"
168
+ BLOCKED = "BLOCKED"
169
+
170
+
171
+ @dataclass(frozen=True, slots=True)
172
+ class SecurityEvidence:
173
+ source: SecurityEvidenceSource
174
+ operation: str
175
+ decision: SecurityDecision
176
+ error_code: str
177
+ resource_summary: str | None
178
+
179
+
180
+ class EvalTraceCollector(Protocol):
181
+ def record_finish(self, finish: TraceRunFinish) -> None: ...
182
+
183
+ def get_finish(self, run_id: str) -> TraceRunFinish | None: ...
184
+
185
+
186
+ @dataclass(frozen=True, slots=True)
187
+ class EvalMetrics:
188
+ agent_steps: int
189
+ llm_calls: int
190
+ tool_calls: int
191
+ replans: int
192
+ repairs: int
193
+ latency_ms: int
194
+ token_usage: TokenUsage
195
+
196
+
197
+ class EvalOutcome(StrEnum):
198
+ PASS = "PASS"
199
+ TASK_FAILED = "TASK_FAILED"
200
+ INFRASTRUCTURE_ERROR = "INFRASTRUCTURE_ERROR"
201
+ EVALUATOR_ERROR = "EVALUATOR_ERROR"
202
+
203
+
204
+ class EvalValidationStatus(StrEnum):
205
+ NOT_RUN = "NOT_RUN"
206
+ PASSED = "PASSED"
207
+ FAILED = "FAILED"
208
+ TIMED_OUT = "TIMED_OUT"
209
+ INFRASTRUCTURE_ERROR = "INFRASTRUCTURE_ERROR"
210
+
211
+
212
+ class EvalPytestNodeOutcome(StrEnum):
213
+ PASSED = "PASSED"
214
+ FAILED = "FAILED"
215
+ SKIPPED = "SKIPPED"
216
+ XFAILED = "XFAILED"
217
+ XPASSED = "XPASSED"
218
+ ERROR = "ERROR"
219
+ NOT_RUN = "NOT_RUN"
220
+
221
+
222
+ @dataclass(frozen=True, slots=True)
223
+ class EvalPytestNodeResult:
224
+ node_id: str
225
+ outcome: EvalPytestNodeOutcome
226
+
227
+
228
+ @dataclass(frozen=True, slots=True)
229
+ class EvalValidationResult:
230
+ status: EvalValidationStatus
231
+ command: tuple[str, ...]
232
+ exit_code: int | None
233
+ stdout_summary: str
234
+ stderr_summary: str
235
+ duration_ms: int
236
+ timeout_seconds: int | None
237
+ infrastructure_error: str | None
238
+ pytest_node_results: tuple[EvalPytestNodeResult, ...] = ()
239
+
240
+
241
+ class EvalDiscoveryStatus(StrEnum):
242
+ NOT_RUN = "NOT_RUN"
243
+ PASSED = "PASSED"
244
+ FAILED = "FAILED"
245
+ TIMED_OUT = "TIMED_OUT"
246
+ INFRASTRUCTURE_ERROR = "INFRASTRUCTURE_ERROR"
247
+
248
+
249
+ @dataclass(frozen=True, slots=True)
250
+ class EvalDiscoveryResult:
251
+ status: EvalDiscoveryStatus
252
+ command: tuple[str, ...]
253
+ exit_code: int | None
254
+ collected_node_ids: tuple[str, ...]
255
+ stdout_summary: str
256
+ stderr_summary: str
257
+ duration_ms: int
258
+ timeout_seconds: int | None
259
+ infrastructure_error: str | None
260
+
261
+
262
+ @dataclass(frozen=True, slots=True)
263
+ class EvalCheckEvidence:
264
+ pre_validation: EvalValidationResult
265
+ post_validation: EvalValidationResult
266
+ pre_discovery: EvalDiscoveryResult
267
+ post_discovery: EvalDiscoveryResult
268
+
269
+
270
+ @dataclass(frozen=True, slots=True)
271
+ class EvalExecutionResult:
272
+ case_id: str
273
+ run_id: str | None
274
+ session_id: str | None
275
+ runtime_status: RuntimeStatus | None
276
+ terminal_status: TerminalStatus | None
277
+ error_code: str | None
278
+ final_result_summary: str | None
279
+ repository: EvalRepositoryEvidence
280
+ checks: EvalCheckEvidence
281
+ metrics: EvalMetrics | None
282
+ security_evidence: tuple[SecurityEvidence, ...]
283
+ infrastructure_error: str | None
284
+
285
+
286
+ @dataclass(frozen=True, slots=True)
287
+ class EvalAssertionResult:
288
+ assertion_type: EvalAssertionType
289
+ passed: bool
290
+ evidence: tuple[str, ...]
291
+ failure_reason: str | None
292
+
293
+
294
+ @dataclass(frozen=True, slots=True)
295
+ class DeterministicEvaluationResult:
296
+ outcome: EvalOutcome
297
+ runtime_completed: bool
298
+ validation_passed: bool | None
299
+ constraints_passed: bool
300
+ forbidden_change_detected: bool
301
+ unauthorized_change_detected: bool
302
+ assertion_results: tuple[EvalAssertionResult, ...]
303
+ failure_reason: str | None
304
+
305
+
306
+ class DeterministicEvaluator(Protocol):
307
+ async def evaluate(
308
+ self, case: EvalCase, execution: EvalExecutionResult
309
+ ) -> DeterministicEvaluationResult: ...
310
+
311
+
312
+ @dataclass(frozen=True, slots=True)
313
+ class EvalCaseLoadError:
314
+ source_path: str
315
+ case_id_hint: str | None
316
+ code: str
317
+ message: str
318
+
319
+
320
+ @dataclass(frozen=True, slots=True)
321
+ class EvalSuiteLoadResult:
322
+ cases: tuple[EvalCase, ...]
323
+ errors: tuple[EvalCaseLoadError, ...]
324
+
325
+
326
+ @dataclass(frozen=True, slots=True)
327
+ class EvalCaseReport:
328
+ case_id: str
329
+ outcome: EvalOutcome
330
+ run_id: str | None
331
+ session_id: str | None
332
+ runtime_status: RuntimeStatus | None
333
+ terminal_status: TerminalStatus | None
334
+ error_code: str | None
335
+ deterministic_result: DeterministicEvaluationResult
336
+ metrics: EvalMetrics | None
337
+ repository: EvalRepositoryEvidence
338
+ checks: EvalCheckEvidence
339
+ security_evidence: tuple[SecurityEvidence, ...]
340
+ efficiency_warning: bool
341
+ evidence: tuple[str, ...]
342
+
343
+
344
+ @dataclass(frozen=True, slots=True)
345
+ class EvalSuiteError:
346
+ code: str
347
+ message: str
348
+ source_path: str | None
349
+ case_id_hint: str | None
350
+
351
+
352
+ @dataclass(frozen=True, slots=True)
353
+ class EvalSuiteReport:
354
+ schema_version: str
355
+ generated_at: datetime
356
+ cases: tuple[EvalCaseReport, ...]
357
+ suite_errors: tuple[EvalSuiteError, ...]
358
+ total_cases: int
359
+ passed_cases: int
360
+ task_failed_cases: int
361
+ infrastructure_error_cases: int
362
+ evaluator_error_cases: int
@@ -0,0 +1,43 @@
1
+ """Stable JSON serialization and report writing."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from collections.abc import Mapping
7
+ from dataclasses import fields, is_dataclass
8
+ from datetime import UTC, datetime
9
+ from enum import Enum
10
+ from pathlib import Path
11
+ from types import MappingProxyType
12
+
13
+ from nexus.evaluation.models import EvalSuiteReport
14
+
15
+
16
+ def write_report(
17
+ report: EvalSuiteReport, reports_root: Path, *, write_baseline: bool = False
18
+ ) -> tuple[Path, Path | None]:
19
+ reports_root.mkdir(parents=True, exist_ok=True)
20
+ stamp = report.generated_at.astimezone(UTC).strftime("%Y%m%dT%H%M%SZ")
21
+ target = reports_root / f"eval-report-{stamp}.json"
22
+ payload = json.dumps(_json_value(report), ensure_ascii=False, indent=2) + "\n"
23
+ target.write_text(payload, encoding="utf-8")
24
+ baseline = reports_root / "baseline-v1.json"
25
+ baseline_written = None
26
+ if write_baseline and not baseline.exists():
27
+ baseline.write_text(payload, encoding="utf-8")
28
+ baseline_written = baseline
29
+ return target, baseline_written
30
+
31
+
32
+ def _json_value(value: object) -> object:
33
+ if is_dataclass(value) and not isinstance(value, type):
34
+ return {item.name: _json_value(getattr(value, item.name)) for item in fields(value)}
35
+ if isinstance(value, Enum):
36
+ return value.value
37
+ if isinstance(value, datetime):
38
+ return value.astimezone(UTC).isoformat().replace("+00:00", "Z")
39
+ if isinstance(value, (Mapping, MappingProxyType)):
40
+ return {str(key): _json_value(item) for key, item in value.items()}
41
+ if isinstance(value, (tuple, list)):
42
+ return [_json_value(item) for item in value]
43
+ return value