mycode-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.
- mycode/__init__.py +0 -0
- mycode/adapters/__init__.py +21 -0
- mycode/adapters/jsonl.py +692 -0
- mycode/agent/__init__.py +25 -0
- mycode/agent/events.py +111 -0
- mycode/agent/outcome.py +103 -0
- mycode/agent/progress.py +373 -0
- mycode/agent/runner.py +1481 -0
- mycode/application/__init__.py +38 -0
- mycode/application/agent_session.py +367 -0
- mycode/application/events.py +59 -0
- mycode/application/runtime.py +211 -0
- mycode/application/sessions.py +180 -0
- mycode/cli.py +840 -0
- mycode/config.py +355 -0
- mycode/context/__init__.py +1 -0
- mycode/context/artifacts.py +672 -0
- mycode/context/budget.py +752 -0
- mycode/context/builder.py +112 -0
- mycode/context/compact.py +795 -0
- mycode/context/tool_result_format.py +199 -0
- mycode/context/tool_result_retention.py +261 -0
- mycode/conversation.py +78 -0
- mycode/error_handling.py +481 -0
- mycode/event_format.py +147 -0
- mycode/instructions.py +285 -0
- mycode/llm.py +771 -0
- mycode/mcp/__init__.py +41 -0
- mycode/mcp/client.py +44 -0
- mycode/mcp/config.py +207 -0
- mycode/mcp/errors.py +302 -0
- mycode/mcp/manager.py +339 -0
- mycode/mcp/models.py +20 -0
- mycode/mcp/result_adapter.py +58 -0
- mycode/mcp/tool_adapter.py +145 -0
- mycode/mcp/trust.py +313 -0
- mycode/memory.py +570 -0
- mycode/memory_context.py +245 -0
- mycode/messages.py +63 -0
- mycode/observability.py +28 -0
- mycode/permissions.py +262 -0
- mycode/persistence/__init__.py +1 -0
- mycode/persistence/filesystem.py +291 -0
- mycode/persistence/project_storage.py +208 -0
- mycode/persistence/session_lock.py +138 -0
- mycode/persistence/session_store.py +503 -0
- mycode/presentation/__init__.py +1 -0
- mycode/presentation/cli/__init__.py +14 -0
- mycode/presentation/cli/confirmer.py +116 -0
- mycode/presentation/cli/mcp_trust.py +61 -0
- mycode/presentation/cli/presenter.py +320 -0
- mycode/presentation/cli/session_menu.py +146 -0
- mycode/presentation/cli/subagent_observer.py +124 -0
- mycode/presentation/command_format.py +90 -0
- mycode/presentation/commands.py +95 -0
- mycode/presentation/tui/__init__.py +6 -0
- mycode/presentation/tui/app.py +1351 -0
- mycode/presentation/tui/interactions.py +253 -0
- mycode/presentation/tui/presenter.py +266 -0
- mycode/presentation/tui/screens.py +305 -0
- mycode/presentation/tui/widgets.py +214 -0
- mycode/project.py +22 -0
- mycode/prompts.py +181 -0
- mycode/reasoning.py +40 -0
- mycode/session.py +86 -0
- mycode/skills/__init__.py +27 -0
- mycode/skills/builtin/database-recovery/SKILL.md +138 -0
- mycode/skills/builtin/database-recovery/references/sqlite.md +235 -0
- mycode/skills/registry.py +295 -0
- mycode/skills/state.py +68 -0
- mycode/subagents/__init__.py +1 -0
- mycode/subagents/audit.py +212 -0
- mycode/subagents/concurrency.py +124 -0
- mycode/subagents/contracts.py +421 -0
- mycode/subagents/delegate.py +80 -0
- mycode/subagents/delegation.py +128 -0
- mycode/subagents/lifecycle.py +86 -0
- mycode/subagents/limits.py +7 -0
- mycode/subagents/observability.py +150 -0
- mycode/subagents/persistence.py +152 -0
- mycode/subagents/profiles.py +184 -0
- mycode/subagents/prompts.py +67 -0
- mycode/subagents/results.py +178 -0
- mycode/subagents/runtime.py +528 -0
- mycode/subagents/snapshots.py +211 -0
- mycode/subagents/tool_batch.py +260 -0
- mycode/tools/__init__.py +81 -0
- mycode/tools/base.py +222 -0
- mycode/tools/bounds.py +14 -0
- mycode/tools/command_executor.py +167 -0
- mycode/tools/command_output.py +166 -0
- mycode/tools/command_risk.py +596 -0
- mycode/tools/defaults.py +59 -0
- mycode/tools/edit_file.py +524 -0
- mycode/tools/file_mutation.py +30 -0
- mycode/tools/glob.py +247 -0
- mycode/tools/grep.py +324 -0
- mycode/tools/ignore.py +122 -0
- mycode/tools/inspect_changes.py +269 -0
- mycode/tools/load_skill.py +92 -0
- mycode/tools/memory.py +264 -0
- mycode/tools/path_permissions.py +78 -0
- mycode/tools/patterns.py +48 -0
- mycode/tools/permission_metadata.py +27 -0
- mycode/tools/process_tree.py +166 -0
- mycode/tools/read_file.py +242 -0
- mycode/tools/read_skill_resource.py +93 -0
- mycode/tools/registry.py +279 -0
- mycode/tools/run_command.py +237 -0
- mycode/tools/run_skill_script.py +206 -0
- mycode/tools/run_validation.py +107 -0
- mycode/tools/submit_result.py +93 -0
- mycode/tools/text.py +15 -0
- mycode/tools/validation_command.py +377 -0
- mycode/tools/workspace.py +33 -0
- mycode/tools/write_file.py +169 -0
- mycode_coding_agent-0.1.0.dist-info/METADATA +244 -0
- mycode_coding_agent-0.1.0.dist-info/RECORD +121 -0
- mycode_coding_agent-0.1.0.dist-info/WHEEL +4 -0
- mycode_coding_agent-0.1.0.dist-info/entry_points.txt +2 -0
- mycode_coding_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from mycode.context.budget import MemoryContextStats
|
|
8
|
+
from mycode.instructions import InstructionBundle, InstructionIssueCode, InstructionScope
|
|
9
|
+
from mycode.memory import MemoryKind, MemoryScope
|
|
10
|
+
from mycode.memory_context import MemoryRecall
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class InstructionSourceFingerprint:
|
|
15
|
+
scope: InstructionScope
|
|
16
|
+
path: Path
|
|
17
|
+
content_bytes: int
|
|
18
|
+
content_chars: int
|
|
19
|
+
sha256: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class InstructionWarningSnapshot:
|
|
24
|
+
path: Path
|
|
25
|
+
code: InstructionIssueCode
|
|
26
|
+
message: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class InstructionSnapshotMetadata:
|
|
31
|
+
loaded_at: datetime
|
|
32
|
+
total_chars: int
|
|
33
|
+
combined_sha256: str
|
|
34
|
+
sources: tuple[InstructionSourceFingerprint, ...] = ()
|
|
35
|
+
warnings: tuple[InstructionWarningSnapshot, ...] = ()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class MemorySourceFingerprint:
|
|
40
|
+
scope: MemoryScope
|
|
41
|
+
path: Path
|
|
42
|
+
content_bytes: int
|
|
43
|
+
content_chars: int
|
|
44
|
+
sha256: str
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class MemoryEntryFingerprint:
|
|
49
|
+
scope: MemoryScope
|
|
50
|
+
kind: MemoryKind
|
|
51
|
+
content_chars: int
|
|
52
|
+
sha256: str
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True)
|
|
56
|
+
class MemorySnapshotMetadata:
|
|
57
|
+
enabled: bool
|
|
58
|
+
loaded_at: datetime
|
|
59
|
+
combined_sha256: str
|
|
60
|
+
stats: MemoryContextStats | None = None
|
|
61
|
+
sources: tuple[MemorySourceFingerprint, ...] = ()
|
|
62
|
+
selected_entries: tuple[MemoryEntryFingerprint, ...] = ()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True)
|
|
66
|
+
class SubAgentSnapshotMetadata:
|
|
67
|
+
loaded_at: datetime
|
|
68
|
+
combined_sha256: str
|
|
69
|
+
instructions: InstructionSnapshotMetadata
|
|
70
|
+
memory: MemorySnapshotMetadata
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass(frozen=True)
|
|
74
|
+
class RuntimeContextSnapshot:
|
|
75
|
+
"""Immutable run-local context; bodies must not be persisted by default."""
|
|
76
|
+
|
|
77
|
+
metadata: SubAgentSnapshotMetadata
|
|
78
|
+
project_instructions: str = field(repr=False, compare=False)
|
|
79
|
+
memory_recall: MemoryRecall | None = field(default=None, repr=False, compare=False)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass(frozen=True)
|
|
83
|
+
class FrozenMemoryRecallProvider:
|
|
84
|
+
recall_snapshot: MemoryRecall
|
|
85
|
+
|
|
86
|
+
def recall(self, user_request: str) -> MemoryRecall:
|
|
87
|
+
return self.recall_snapshot
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def create_runtime_context_snapshot(
|
|
91
|
+
instruction_bundle: InstructionBundle,
|
|
92
|
+
*,
|
|
93
|
+
memory_recall: MemoryRecall | None,
|
|
94
|
+
loaded_at: datetime,
|
|
95
|
+
) -> RuntimeContextSnapshot:
|
|
96
|
+
project_instructions = instruction_bundle.to_prompt_text()
|
|
97
|
+
instruction_metadata = InstructionSnapshotMetadata(
|
|
98
|
+
loaded_at=loaded_at,
|
|
99
|
+
total_chars=instruction_bundle.total_chars,
|
|
100
|
+
combined_sha256=_sha256_text(project_instructions),
|
|
101
|
+
sources=tuple(
|
|
102
|
+
InstructionSourceFingerprint(
|
|
103
|
+
scope=source.scope,
|
|
104
|
+
path=source.path,
|
|
105
|
+
content_bytes=(
|
|
106
|
+
source.content_bytes
|
|
107
|
+
if source.content_bytes > 0
|
|
108
|
+
else len(source.content.encode("utf-8"))
|
|
109
|
+
),
|
|
110
|
+
content_chars=len(source.content),
|
|
111
|
+
sha256=source.sha256 or _sha256_text(source.content),
|
|
112
|
+
)
|
|
113
|
+
for source in instruction_bundle.sources
|
|
114
|
+
),
|
|
115
|
+
warnings=tuple(
|
|
116
|
+
InstructionWarningSnapshot(
|
|
117
|
+
path=issue.path,
|
|
118
|
+
code=issue.code,
|
|
119
|
+
message=issue.message,
|
|
120
|
+
)
|
|
121
|
+
for issue in instruction_bundle.issues
|
|
122
|
+
),
|
|
123
|
+
)
|
|
124
|
+
memory_metadata = _memory_snapshot_metadata(memory_recall, loaded_at=loaded_at)
|
|
125
|
+
combined_sha256 = _sha256_json(
|
|
126
|
+
{
|
|
127
|
+
"instructions": instruction_metadata.combined_sha256,
|
|
128
|
+
"memory": memory_metadata.combined_sha256,
|
|
129
|
+
"memory_enabled": memory_metadata.enabled,
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
return RuntimeContextSnapshot(
|
|
133
|
+
metadata=SubAgentSnapshotMetadata(
|
|
134
|
+
loaded_at=loaded_at,
|
|
135
|
+
combined_sha256=combined_sha256,
|
|
136
|
+
instructions=instruction_metadata,
|
|
137
|
+
memory=memory_metadata,
|
|
138
|
+
),
|
|
139
|
+
project_instructions=project_instructions,
|
|
140
|
+
memory_recall=memory_recall,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _memory_snapshot_metadata(
|
|
145
|
+
recall: MemoryRecall | None,
|
|
146
|
+
*,
|
|
147
|
+
loaded_at: datetime,
|
|
148
|
+
) -> MemorySnapshotMetadata:
|
|
149
|
+
if recall is None:
|
|
150
|
+
return MemorySnapshotMetadata(
|
|
151
|
+
enabled=False,
|
|
152
|
+
loaded_at=loaded_at,
|
|
153
|
+
combined_sha256=_sha256_text(""),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
selected_entries = tuple(
|
|
157
|
+
MemoryEntryFingerprint(
|
|
158
|
+
scope=entry.scope,
|
|
159
|
+
kind=entry.kind,
|
|
160
|
+
content_chars=len(entry.content),
|
|
161
|
+
sha256=_sha256_json(
|
|
162
|
+
{
|
|
163
|
+
"scope": entry.scope,
|
|
164
|
+
"kind": entry.kind,
|
|
165
|
+
"key": entry.key,
|
|
166
|
+
"content": entry.content,
|
|
167
|
+
}
|
|
168
|
+
),
|
|
169
|
+
)
|
|
170
|
+
for entry in recall.entries
|
|
171
|
+
)
|
|
172
|
+
sources = tuple(
|
|
173
|
+
MemorySourceFingerprint(
|
|
174
|
+
scope=source.scope,
|
|
175
|
+
path=source.path,
|
|
176
|
+
content_bytes=source.content_bytes,
|
|
177
|
+
content_chars=source.content_chars,
|
|
178
|
+
sha256=source.sha256,
|
|
179
|
+
)
|
|
180
|
+
for source in recall.sources
|
|
181
|
+
)
|
|
182
|
+
message_content = "" if recall.message is None else recall.message.content
|
|
183
|
+
return MemorySnapshotMetadata(
|
|
184
|
+
enabled=True,
|
|
185
|
+
loaded_at=loaded_at,
|
|
186
|
+
combined_sha256=_sha256_json(
|
|
187
|
+
{
|
|
188
|
+
"message": message_content,
|
|
189
|
+
"sources": [source.sha256 for source in sources],
|
|
190
|
+
"entries": [entry.sha256 for entry in selected_entries],
|
|
191
|
+
}
|
|
192
|
+
),
|
|
193
|
+
stats=recall.stats,
|
|
194
|
+
sources=sources,
|
|
195
|
+
selected_entries=selected_entries,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _sha256_text(content: str) -> str:
|
|
200
|
+
return hashlib.sha256(content.encode("utf-8")).hexdigest()
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _sha256_json(value: object) -> str:
|
|
204
|
+
return _sha256_text(
|
|
205
|
+
json.dumps(
|
|
206
|
+
value,
|
|
207
|
+
ensure_ascii=False,
|
|
208
|
+
sort_keys=True,
|
|
209
|
+
separators=(",", ":"),
|
|
210
|
+
)
|
|
211
|
+
)
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from pydantic import ValidationError
|
|
6
|
+
|
|
7
|
+
from mycode.agent.events import AgentModelResponse, AgentToolCall
|
|
8
|
+
from mycode.agent.runner import (
|
|
9
|
+
ToolBatchExecution,
|
|
10
|
+
ToolCallExecution,
|
|
11
|
+
append_tool_call_limit_failures,
|
|
12
|
+
execute_tool_batch,
|
|
13
|
+
partition_tool_calls_by_limit,
|
|
14
|
+
)
|
|
15
|
+
from mycode.subagents.audit import SubAgentToolAudit, build_tool_audit
|
|
16
|
+
from mycode.subagents.contracts import (
|
|
17
|
+
BoundedResultArgs,
|
|
18
|
+
SubAgentPayload,
|
|
19
|
+
SubAgentRole,
|
|
20
|
+
ValidationExecution,
|
|
21
|
+
)
|
|
22
|
+
from mycode.subagents.results import (
|
|
23
|
+
ResultCompressionError,
|
|
24
|
+
finalize_submitted_payload,
|
|
25
|
+
)
|
|
26
|
+
from mycode.tools.base import ToolResult
|
|
27
|
+
from mycode.tools.registry import ToolRegistry
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
ToolAuditHandler = Callable[[SubAgentToolAudit], None]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class SubAgentToolBatchHandler:
|
|
35
|
+
role: SubAgentRole
|
|
36
|
+
max_final_payload_chars: int
|
|
37
|
+
max_validation_calls: int
|
|
38
|
+
audit_handler: ToolAuditHandler | None = None
|
|
39
|
+
submitted_payload: SubAgentPayload | None = None
|
|
40
|
+
submission_attempted: bool = False
|
|
41
|
+
tool_call_count: int = 0
|
|
42
|
+
validation_call_count: int = 0
|
|
43
|
+
validation_executions: list[ValidationExecution] = field(default_factory=list)
|
|
44
|
+
validation_evidence_errors: list[str] = field(default_factory=list)
|
|
45
|
+
|
|
46
|
+
def __call__(
|
|
47
|
+
self,
|
|
48
|
+
registry: ToolRegistry,
|
|
49
|
+
tool_calls: list[AgentToolCall],
|
|
50
|
+
) -> ToolBatchExecution:
|
|
51
|
+
self.tool_call_count += len(tool_calls)
|
|
52
|
+
submission_calls = [
|
|
53
|
+
tool_call
|
|
54
|
+
for tool_call in tool_calls
|
|
55
|
+
if tool_call.name == "submit_result"
|
|
56
|
+
]
|
|
57
|
+
executable_calls, overflow_executions = partition_tool_calls_by_limit(
|
|
58
|
+
tool_calls
|
|
59
|
+
)
|
|
60
|
+
if len(submission_calls) > 1:
|
|
61
|
+
self.submission_attempted = True
|
|
62
|
+
batch = ToolBatchExecution(
|
|
63
|
+
executions=tuple(
|
|
64
|
+
ToolCallExecution(
|
|
65
|
+
tool_call=tool_call,
|
|
66
|
+
result=_multiple_submission_result(tool_call),
|
|
67
|
+
)
|
|
68
|
+
for tool_call in executable_calls
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
elif submission_calls:
|
|
72
|
+
self.submission_attempted = True
|
|
73
|
+
batch = self._execute_submission_barrier(
|
|
74
|
+
registry,
|
|
75
|
+
executable_calls,
|
|
76
|
+
submission_calls[0],
|
|
77
|
+
)
|
|
78
|
+
else:
|
|
79
|
+
batch = self._execute_regular_batch(registry, tool_calls)
|
|
80
|
+
self._emit_audits(batch)
|
|
81
|
+
return batch
|
|
82
|
+
batch = append_tool_call_limit_failures(batch, overflow_executions)
|
|
83
|
+
self._emit_audits(batch)
|
|
84
|
+
return batch
|
|
85
|
+
|
|
86
|
+
def validate_submission(self, submitted: BoundedResultArgs) -> str | None:
|
|
87
|
+
if self.validation_evidence_errors:
|
|
88
|
+
return (
|
|
89
|
+
"Runtime could not form trustworthy validation evidence: "
|
|
90
|
+
+ "; ".join(self.validation_evidence_errors[:3])
|
|
91
|
+
)
|
|
92
|
+
try:
|
|
93
|
+
payload = finalize_submitted_payload(
|
|
94
|
+
self.role,
|
|
95
|
+
submitted,
|
|
96
|
+
validation_executions=self.validation_executions,
|
|
97
|
+
max_chars=self.max_final_payload_chars,
|
|
98
|
+
)
|
|
99
|
+
except (ValidationError, ResultCompressionError, ValueError) as error:
|
|
100
|
+
return _submission_validation_error(error)
|
|
101
|
+
self.submitted_payload = payload
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
def _execute_submission_barrier(
|
|
105
|
+
self,
|
|
106
|
+
registry: ToolRegistry,
|
|
107
|
+
tool_calls: list[AgentToolCall],
|
|
108
|
+
submission_call: AgentToolCall,
|
|
109
|
+
) -> ToolBatchExecution:
|
|
110
|
+
self.submission_attempted = True
|
|
111
|
+
executions: list[ToolCallExecution] = []
|
|
112
|
+
submission_result: ToolResult | None = None
|
|
113
|
+
submission_is_executable = any(
|
|
114
|
+
tool_call is submission_call for tool_call in tool_calls
|
|
115
|
+
)
|
|
116
|
+
for tool_call in tool_calls:
|
|
117
|
+
if tool_call is submission_call and submission_is_executable:
|
|
118
|
+
submission_result = registry.run_tool(
|
|
119
|
+
tool_call.name,
|
|
120
|
+
tool_call.arguments,
|
|
121
|
+
)
|
|
122
|
+
result = submission_result
|
|
123
|
+
else:
|
|
124
|
+
result = ToolResult.failure(
|
|
125
|
+
error=(
|
|
126
|
+
"Tool call skipped because submit_result is a control-flow "
|
|
127
|
+
"barrier and must be handled first."
|
|
128
|
+
),
|
|
129
|
+
metadata={
|
|
130
|
+
"tool_name": tool_call.name,
|
|
131
|
+
"reason": "skipped_due_to_submission_barrier",
|
|
132
|
+
},
|
|
133
|
+
)
|
|
134
|
+
executions.append(ToolCallExecution(tool_call=tool_call, result=result))
|
|
135
|
+
|
|
136
|
+
stop_response = None
|
|
137
|
+
if submission_result is not None and submission_result.ok:
|
|
138
|
+
stop_response = AgentModelResponse(
|
|
139
|
+
content="Structured SubAgent result submitted.",
|
|
140
|
+
stop_reason="control_tool",
|
|
141
|
+
)
|
|
142
|
+
return ToolBatchExecution(
|
|
143
|
+
executions=tuple(executions),
|
|
144
|
+
stop_response=stop_response,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
def _execute_regular_batch(
|
|
148
|
+
self,
|
|
149
|
+
registry: ToolRegistry,
|
|
150
|
+
tool_calls: list[AgentToolCall],
|
|
151
|
+
) -> ToolBatchExecution:
|
|
152
|
+
return execute_tool_batch(
|
|
153
|
+
registry,
|
|
154
|
+
tool_calls,
|
|
155
|
+
serial_executor=lambda tool_call: self._execute_serial_tool(
|
|
156
|
+
registry,
|
|
157
|
+
tool_call,
|
|
158
|
+
),
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def _execute_serial_tool(
|
|
162
|
+
self,
|
|
163
|
+
registry: ToolRegistry,
|
|
164
|
+
tool_call: AgentToolCall,
|
|
165
|
+
) -> ToolResult:
|
|
166
|
+
if tool_call.name == "run_validation":
|
|
167
|
+
return self._run_validation(registry, tool_call)
|
|
168
|
+
return registry.run_tool(tool_call.name, tool_call.arguments)
|
|
169
|
+
|
|
170
|
+
def _run_validation(
|
|
171
|
+
self,
|
|
172
|
+
registry: ToolRegistry,
|
|
173
|
+
tool_call: AgentToolCall,
|
|
174
|
+
) -> ToolResult:
|
|
175
|
+
self.validation_call_count += 1
|
|
176
|
+
if self.validation_call_count > self.max_validation_calls:
|
|
177
|
+
return ToolResult.failure(
|
|
178
|
+
error=(
|
|
179
|
+
"Validation call limit reached for this SubAgent run: "
|
|
180
|
+
f"{self.max_validation_calls}."
|
|
181
|
+
),
|
|
182
|
+
metadata={
|
|
183
|
+
"tool_name": tool_call.name,
|
|
184
|
+
"reason": "validation_call_limit",
|
|
185
|
+
"max_validation_calls": self.max_validation_calls,
|
|
186
|
+
},
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
result = registry.run_tool(tool_call.name, tool_call.arguments)
|
|
190
|
+
self._record_validation_execution(tool_call, result)
|
|
191
|
+
return result
|
|
192
|
+
|
|
193
|
+
def _record_validation_execution(
|
|
194
|
+
self,
|
|
195
|
+
tool_call: AgentToolCall,
|
|
196
|
+
result: ToolResult,
|
|
197
|
+
) -> None:
|
|
198
|
+
metadata = result.metadata
|
|
199
|
+
if "exit_code" not in metadata:
|
|
200
|
+
return
|
|
201
|
+
try:
|
|
202
|
+
command = metadata.get("command", tool_call.arguments.get("command"))
|
|
203
|
+
cwd = metadata.get("resolved_cwd", tool_call.arguments.get("cwd", "."))
|
|
204
|
+
execution = ValidationExecution.model_validate(
|
|
205
|
+
{
|
|
206
|
+
"command": command,
|
|
207
|
+
"cwd": str(cwd),
|
|
208
|
+
"exit_code": metadata.get("exit_code"),
|
|
209
|
+
"duration_ms": metadata.get("duration_ms", 0),
|
|
210
|
+
"timed_out": metadata.get("timed_out", False),
|
|
211
|
+
}
|
|
212
|
+
)
|
|
213
|
+
except ValidationError as error:
|
|
214
|
+
self.validation_evidence_errors.append(_submission_validation_error(error))
|
|
215
|
+
return
|
|
216
|
+
self.validation_executions.append(execution)
|
|
217
|
+
|
|
218
|
+
def _emit_audits(self, batch: ToolBatchExecution) -> None:
|
|
219
|
+
if self.audit_handler is None:
|
|
220
|
+
return
|
|
221
|
+
for execution in batch.executions:
|
|
222
|
+
self.audit_handler(
|
|
223
|
+
build_tool_audit(execution.tool_call, execution.result)
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _multiple_submission_result(tool_call: AgentToolCall) -> ToolResult:
|
|
228
|
+
if tool_call.name == "submit_result":
|
|
229
|
+
return ToolResult.failure(
|
|
230
|
+
error="Multiple submit_result calls in one model response are not allowed.",
|
|
231
|
+
metadata={
|
|
232
|
+
"tool_name": tool_call.name,
|
|
233
|
+
"reason": "multiple_submission_barriers",
|
|
234
|
+
},
|
|
235
|
+
)
|
|
236
|
+
return ToolResult.failure(
|
|
237
|
+
error="Tool call skipped because the submission batch is invalid.",
|
|
238
|
+
metadata={
|
|
239
|
+
"tool_name": tool_call.name,
|
|
240
|
+
"reason": "skipped_due_to_submission_batch_error",
|
|
241
|
+
},
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _submission_validation_error(error: Exception) -> str:
|
|
246
|
+
if isinstance(error, ValidationError):
|
|
247
|
+
details = [
|
|
248
|
+
{
|
|
249
|
+
"location": ".".join(str(item) for item in detail["loc"]),
|
|
250
|
+
"message": detail["msg"],
|
|
251
|
+
"type": detail["type"],
|
|
252
|
+
}
|
|
253
|
+
for detail in error.errors(include_input=False)[:5]
|
|
254
|
+
]
|
|
255
|
+
return "Runtime result validation failed: " + json.dumps(
|
|
256
|
+
details,
|
|
257
|
+
ensure_ascii=False,
|
|
258
|
+
separators=(",", ":"),
|
|
259
|
+
)
|
|
260
|
+
return f"Runtime result validation failed: {error}"
|
mycode/tools/__init__.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from mycode.tools.base import (
|
|
2
|
+
BaseTool,
|
|
3
|
+
PydanticTool,
|
|
4
|
+
SyncTool,
|
|
5
|
+
ToolArgumentValidationError,
|
|
6
|
+
ToolArgs,
|
|
7
|
+
ToolPermissionProfileError,
|
|
8
|
+
ToolResult,
|
|
9
|
+
)
|
|
10
|
+
from mycode.tools.defaults import create_default_tool_registry, create_read_only_tool_registry
|
|
11
|
+
from mycode.tools.edit_file import EditFileArgs, EditFileTool
|
|
12
|
+
from mycode.tools.glob import GlobArgs, GlobTool
|
|
13
|
+
from mycode.tools.grep import GrepArgs, GrepTool
|
|
14
|
+
from mycode.tools.memory import (
|
|
15
|
+
DeleteMemoryArgs,
|
|
16
|
+
DeleteMemoryTool,
|
|
17
|
+
ListMemoriesArgs,
|
|
18
|
+
ListMemoriesTool,
|
|
19
|
+
SaveMemoryArgs,
|
|
20
|
+
SaveMemoryTool,
|
|
21
|
+
)
|
|
22
|
+
from mycode.tools.path_permissions import PathPermissionPolicy
|
|
23
|
+
from mycode.tools.read_file import ReadFileArgs, ReadFileTool
|
|
24
|
+
from mycode.tools.registry import DuplicateToolError, ToolNotFoundError, ToolRegistry
|
|
25
|
+
from mycode.tools.inspect_changes import InspectChangesArgs, InspectChangesTool
|
|
26
|
+
from mycode.tools.load_skill import LoadSkillArgs, LoadSkillTool
|
|
27
|
+
from mycode.tools.read_skill_resource import (
|
|
28
|
+
ReadSkillResourceArgs,
|
|
29
|
+
ReadSkillResourceTool,
|
|
30
|
+
)
|
|
31
|
+
from mycode.tools.run_command import RunCommandArgs, RunCommandTool
|
|
32
|
+
from mycode.tools.run_skill_script import RunSkillScriptArgs, RunSkillScriptTool
|
|
33
|
+
from mycode.tools.run_validation import RunValidationArgs, RunValidationTool
|
|
34
|
+
from mycode.tools.workspace import Workspace, WorkspacePathError
|
|
35
|
+
from mycode.tools.write_file import WriteFileArgs, WriteFileTool
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"BaseTool",
|
|
39
|
+
"PydanticTool",
|
|
40
|
+
"SyncTool",
|
|
41
|
+
"ToolArgumentValidationError",
|
|
42
|
+
"DuplicateToolError",
|
|
43
|
+
"EditFileArgs",
|
|
44
|
+
"EditFileTool",
|
|
45
|
+
"GlobArgs",
|
|
46
|
+
"GlobTool",
|
|
47
|
+
"GrepArgs",
|
|
48
|
+
"GrepTool",
|
|
49
|
+
"InspectChangesArgs",
|
|
50
|
+
"InspectChangesTool",
|
|
51
|
+
"DeleteMemoryArgs",
|
|
52
|
+
"DeleteMemoryTool",
|
|
53
|
+
"ListMemoriesArgs",
|
|
54
|
+
"ListMemoriesTool",
|
|
55
|
+
"LoadSkillArgs",
|
|
56
|
+
"LoadSkillTool",
|
|
57
|
+
"PathPermissionPolicy",
|
|
58
|
+
"ReadFileArgs",
|
|
59
|
+
"ReadFileTool",
|
|
60
|
+
"ReadSkillResourceArgs",
|
|
61
|
+
"ReadSkillResourceTool",
|
|
62
|
+
"RunCommandArgs",
|
|
63
|
+
"RunCommandTool",
|
|
64
|
+
"RunSkillScriptArgs",
|
|
65
|
+
"RunSkillScriptTool",
|
|
66
|
+
"RunValidationArgs",
|
|
67
|
+
"RunValidationTool",
|
|
68
|
+
"SaveMemoryArgs",
|
|
69
|
+
"SaveMemoryTool",
|
|
70
|
+
"ToolArgs",
|
|
71
|
+
"ToolNotFoundError",
|
|
72
|
+
"ToolPermissionProfileError",
|
|
73
|
+
"ToolRegistry",
|
|
74
|
+
"ToolResult",
|
|
75
|
+
"Workspace",
|
|
76
|
+
"WorkspacePathError",
|
|
77
|
+
"WriteFileArgs",
|
|
78
|
+
"WriteFileTool",
|
|
79
|
+
"create_default_tool_registry",
|
|
80
|
+
"create_read_only_tool_registry",
|
|
81
|
+
]
|