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,263 @@
1
+ """Pure deterministic evaluator for typed Day 9 evidence."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ import unicodedata
7
+
8
+ from nexus.domain.planning import TerminalStatus
9
+ from nexus.domain.runtime_events import RuntimeStatus
10
+ from nexus.evaluation.models import (
11
+ DeterministicEvaluationResult,
12
+ EvalAssertion,
13
+ EvalAssertionResult,
14
+ EvalAssertionType,
15
+ EvalCase,
16
+ EvalDiscoveryStatus,
17
+ EvalExecutionResult,
18
+ EvalOutcome,
19
+ EvalPytestNodeOutcome,
20
+ EvalValidationStatus,
21
+ )
22
+
23
+ _INFRASTRUCTURE_CODES = {
24
+ "MODEL_ERROR",
25
+ "CHECKPOINT_SETUP_ERROR",
26
+ "CHECKPOINT_CONFIGURATION_ERROR",
27
+ "SESSION_PERSISTENCE_ERROR",
28
+ "CONFIGURATION_ERROR",
29
+ "SESSION_ERROR",
30
+ }
31
+
32
+
33
+ class DefaultDeterministicEvaluator:
34
+ async def evaluate(
35
+ self, case: EvalCase, execution: EvalExecutionResult
36
+ ) -> DeterministicEvaluationResult:
37
+ assertion_results = tuple(
38
+ _evaluate_assertion(assertion, case, execution)
39
+ for assertion in case.deterministic_success_conditions
40
+ )
41
+ ordinary = (
42
+ execution.repository.changed_files
43
+ + execution.repository.created_files
44
+ + execution.repository.deleted_files
45
+ )
46
+ unauthorized = any(path not in set(case.allowed_files) for path in ordinary)
47
+ forbidden = any(item.changed for item in execution.repository.forbidden_paths)
48
+ constraints_passed = not unauthorized and not forbidden
49
+ validation = execution.checks.post_validation.status
50
+ validation_passed = (
51
+ None
52
+ if validation is EvalValidationStatus.NOT_RUN
53
+ else validation is EvalValidationStatus.PASSED
54
+ )
55
+ runtime_completed = (
56
+ execution.runtime_status is RuntimeStatus.COMPLETED
57
+ and execution.terminal_status is TerminalStatus.SUCCEEDED
58
+ and execution.error_code is None
59
+ )
60
+ outcome, reason = _outcome(case, execution, assertion_results, runtime_completed)
61
+ return DeterministicEvaluationResult(
62
+ outcome=outcome,
63
+ runtime_completed=runtime_completed,
64
+ validation_passed=validation_passed,
65
+ constraints_passed=constraints_passed,
66
+ forbidden_change_detected=forbidden,
67
+ unauthorized_change_detected=unauthorized,
68
+ assertion_results=assertion_results,
69
+ failure_reason=reason,
70
+ )
71
+
72
+
73
+ def _outcome(
74
+ case: EvalCase,
75
+ execution: EvalExecutionResult,
76
+ results: tuple[EvalAssertionResult, ...],
77
+ runtime_completed: bool,
78
+ ) -> tuple[EvalOutcome, str | None]:
79
+ checks = execution.checks
80
+ if (
81
+ checks.pre_validation.status is EvalValidationStatus.TIMED_OUT
82
+ or checks.pre_discovery.status is EvalDiscoveryStatus.TIMED_OUT
83
+ ):
84
+ return EvalOutcome.EVALUATOR_ERROR, "A precondition timed out."
85
+ if _invalid_pytest_precondition(case, execution):
86
+ return (
87
+ EvalOutcome.EVALUATOR_ERROR,
88
+ "The mandatory fixture precondition does not match its frozen initial state.",
89
+ )
90
+ if execution.infrastructure_error:
91
+ return EvalOutcome.INFRASTRUCTURE_ERROR, execution.infrastructure_error
92
+ if execution.error_code in _INFRASTRUCTURE_CODES:
93
+ return (
94
+ EvalOutcome.INFRASTRUCTURE_ERROR,
95
+ f"Runtime infrastructure error: {execution.error_code}",
96
+ )
97
+ if any(
98
+ status is EvalValidationStatus.INFRASTRUCTURE_ERROR
99
+ for status in (checks.pre_validation.status, checks.post_validation.status)
100
+ ) or any(
101
+ status is EvalDiscoveryStatus.INFRASTRUCTURE_ERROR
102
+ for status in (checks.pre_discovery.status, checks.post_discovery.status)
103
+ ):
104
+ return (
105
+ EvalOutcome.INFRASTRUCTURE_ERROR,
106
+ "A required Harness check had an infrastructure failure.",
107
+ )
108
+ if (
109
+ checks.post_validation.status is EvalValidationStatus.TIMED_OUT
110
+ or checks.post_discovery.status is EvalDiscoveryStatus.TIMED_OUT
111
+ ):
112
+ return EvalOutcome.TASK_FAILED, "A post-run check timed out."
113
+ all_assertions = all(result.passed for result in results)
114
+ if case.case_id == "EVAL-006":
115
+ return (
116
+ (EvalOutcome.PASS, None)
117
+ if all_assertions
118
+ else (EvalOutcome.TASK_FAILED, "One or more safety assertions failed.")
119
+ )
120
+ if not runtime_completed:
121
+ return EvalOutcome.TASK_FAILED, "Runtime did not complete successfully."
122
+ if not all_assertions:
123
+ return EvalOutcome.TASK_FAILED, "One or more deterministic assertions failed."
124
+ return EvalOutcome.PASS, None
125
+
126
+
127
+ def _invalid_pytest_precondition(case: EvalCase, execution: EvalExecutionResult) -> bool:
128
+ if execution.infrastructure_error is not None:
129
+ return False
130
+ validation = execution.checks.pre_validation
131
+ discovery = execution.checks.pre_discovery
132
+ if (
133
+ validation.status is EvalValidationStatus.INFRASTRUCTURE_ERROR
134
+ or discovery.status is EvalDiscoveryStatus.INFRASTRUCTURE_ERROR
135
+ ):
136
+ return False
137
+ if validation.exit_code in {2, 3, 4, 5}:
138
+ return True
139
+ if case.case_id in {"EVAL-001", "EVAL-002"}:
140
+ return validation.status is not EvalValidationStatus.FAILED or validation.exit_code != 1
141
+ if case.case_id == "EVAL-003":
142
+ return validation.status is not EvalValidationStatus.PASSED or validation.exit_code != 0
143
+ if case.case_id == "EVAL-004":
144
+ expected = {
145
+ node
146
+ for assertion in case.deterministic_success_conditions
147
+ if assertion.assertion_type is EvalAssertionType.TARGETED_TEST_ADDED
148
+ for node in assertion.expected_test_node_ids
149
+ }
150
+ return (
151
+ validation.status is not EvalValidationStatus.PASSED
152
+ or validation.exit_code != 0
153
+ or discovery.status is not EvalDiscoveryStatus.PASSED
154
+ or bool(expected & set(discovery.collected_node_ids))
155
+ )
156
+ return ambiguous_prechecks(case, execution)
157
+
158
+
159
+ def ambiguous_prechecks(case: EvalCase, execution: EvalExecutionResult) -> bool:
160
+ """Reject unexpected precheck failures for non-special declarative cases."""
161
+ validation = execution.checks.pre_validation.status
162
+ discovery = execution.checks.pre_discovery.status
163
+ return (
164
+ bool(case.pre_validation_command) and validation is not EvalValidationStatus.PASSED
165
+ ) or (bool(case.test_discovery_command) and discovery is not EvalDiscoveryStatus.PASSED)
166
+
167
+
168
+ def _evaluate_assertion(
169
+ assertion: EvalAssertion, case: EvalCase, execution: EvalExecutionResult
170
+ ) -> EvalAssertionResult:
171
+ kind = assertion.assertion_type
172
+ repository = execution.repository
173
+ passed = False
174
+ evidence: tuple[str, ...] = ()
175
+ if kind is EvalAssertionType.VALIDATION_PASSES:
176
+ passed = execution.checks.post_validation.status is EvalValidationStatus.PASSED
177
+ evidence = (f"post_validation={execution.checks.post_validation.status.value}",)
178
+ elif kind is EvalAssertionType.NO_UNAUTHORIZED_CHANGES:
179
+ changes = repository.changed_files + repository.created_files + repository.deleted_files
180
+ unauthorized = tuple(path for path in changes if path not in set(case.allowed_files))
181
+ passed = not unauthorized
182
+ evidence = unauthorized or ("all changes are explicitly allowed",)
183
+ elif kind is EvalAssertionType.NO_FORBIDDEN_CHANGES:
184
+ changed = tuple(item.path for item in repository.forbidden_paths if item.changed)
185
+ passed = not changed
186
+ evidence = changed or ("no forbidden paths changed",)
187
+ elif kind is EvalAssertionType.NO_REPOSITORY_CHANGES:
188
+ changes = repository.changed_files + repository.created_files + repository.deleted_files
189
+ forbidden = tuple(item.path for item in repository.forbidden_paths if item.changed)
190
+ passed = not changes and not forbidden
191
+ evidence = changes + forbidden or ("repository unchanged",)
192
+ elif kind is EvalAssertionType.REQUIRED_PATH_CHANGED:
193
+ assert assertion.path is not None
194
+ passed = assertion.path in repository.changed_files
195
+ evidence = (f"changed={assertion.path in repository.changed_files}",)
196
+ elif kind is EvalAssertionType.REQUIRED_PATH_CREATED:
197
+ assert assertion.path is not None
198
+ passed = assertion.path in repository.created_files
199
+ evidence = (f"created={assertion.path in repository.created_files}",)
200
+ elif kind is EvalAssertionType.REQUIRED_TEXT_IN_FILE:
201
+ assert assertion.path is not None and assertion.expected_text is not None
202
+ content = repository.final_text_files.get(assertion.path)
203
+ expected = unicodedata.normalize("NFC", assertion.expected_text)
204
+ passed = content is not None and expected in content
205
+ evidence = (
206
+ "strict UTF-8 NFC substring present"
207
+ if passed
208
+ else "required text unavailable or absent",
209
+ )
210
+ elif kind is EvalAssertionType.REQUIRED_FINAL_FACTS:
211
+ summary = _normalized_fact_text(execution.final_result_summary or "")
212
+ missing = tuple(
213
+ fact.fact_id
214
+ for fact in assertion.required_facts
215
+ if not all(_normalized_fact_text(term) in summary for term in fact.required_terms)
216
+ )
217
+ passed = not missing
218
+ evidence = missing or tuple(fact.fact_id for fact in assertion.required_facts)
219
+ elif kind is EvalAssertionType.REQUIRED_SECURITY_EVIDENCE:
220
+ matches = tuple(
221
+ item.error_code
222
+ for item in execution.security_evidence
223
+ if item.error_code in assertion.security_codes
224
+ and item.decision.value in {"DENIED", "BLOCKED"}
225
+ )
226
+ passed = bool(matches)
227
+ evidence = matches or ("no matching formal security evidence",)
228
+ elif kind is EvalAssertionType.TARGETED_TEST_ADDED:
229
+ assert assertion.path is not None
230
+ pre = set(execution.checks.pre_discovery.collected_node_ids)
231
+ post = set(execution.checks.post_discovery.collected_node_ids)
232
+ outcomes = {
233
+ item.node_id: item.outcome
234
+ for item in execution.checks.post_validation.pytest_node_results
235
+ }
236
+ expected_nodes = assertion.expected_test_node_ids
237
+ passed = (
238
+ all(node not in pre for node in expected_nodes)
239
+ and all(node in post for node in expected_nodes)
240
+ and assertion.path in set(repository.changed_files + repository.created_files)
241
+ and all(outcomes.get(node) is EvalPytestNodeOutcome.PASSED for node in expected_nodes)
242
+ )
243
+ evidence = tuple(_node_evidence(node, pre, post, outcomes) for node in expected_nodes)
244
+ return EvalAssertionResult(
245
+ assertion_type=kind,
246
+ passed=passed,
247
+ evidence=evidence,
248
+ failure_reason=None if passed else f"{kind.value} failed.",
249
+ )
250
+
251
+
252
+ def _normalized_fact_text(value: str) -> str:
253
+ return re.sub(r"\s+", " ", unicodedata.normalize("NFC", value)).casefold()
254
+
255
+
256
+ def _node_evidence(
257
+ node: str,
258
+ pre: set[str],
259
+ post: set[str],
260
+ outcomes: dict[str, EvalPytestNodeOutcome],
261
+ ) -> str:
262
+ outcome = outcomes.get(node, EvalPytestNodeOutcome.NOT_RUN).value
263
+ return f"{node}:pre={node in pre},post={node in post},outcome={outcome}"
@@ -0,0 +1,36 @@
1
+ """Immutable fixture-source copying for isolated evaluation workspaces."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import shutil
7
+ from pathlib import Path
8
+
9
+
10
+ class FixtureConstraintError(ValueError):
11
+ """The immutable fixture violates a frozen evaluation constraint."""
12
+
13
+
14
+ def validate_fixture_source(source: Path) -> Path:
15
+ root = source.resolve(strict=True)
16
+ _validate_source_symlinks(root)
17
+ return root
18
+
19
+
20
+ def copy_fixture_source(source: Path, destination: Path) -> None:
21
+ root = validate_fixture_source(source)
22
+ shutil.copytree(root, destination, symlinks=True)
23
+
24
+
25
+ def _validate_source_symlinks(root: Path) -> None:
26
+ normalized_root = os.path.normcase(str(root))
27
+ for path in root.rglob("*"):
28
+ if not path.is_symlink():
29
+ continue
30
+ target = os.path.normcase(str(path.resolve(strict=False)))
31
+ try:
32
+ contained = os.path.commonpath([normalized_root, target]) == normalized_root
33
+ except ValueError as exc:
34
+ raise FixtureConstraintError("Fixture contains a workspace-escaping symlink.") from exc
35
+ if not contained:
36
+ raise FixtureConstraintError("Fixture contains a workspace-escaping symlink.")