kryth 0.0.36__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.
- agent/__init__.py +6 -0
- agent/agent.py +253 -0
- agent/events.py +161 -0
- agent/session.py +294 -0
- agent/store.py +362 -0
- client/__init__.py +23 -0
- client/llm_client.py +376 -0
- client/response.py +87 -0
- config/__init__.py +6 -0
- config/config.py +288 -0
- config/credentials.py +54 -0
- config/keyring_store.py +264 -0
- config/loader.py +207 -0
- config/oauth.py +137 -0
- config/providers.py +167 -0
- config/setup_wizard.py +387 -0
- context/__init__.py +30 -0
- context/compaction.py +106 -0
- context/loop_detector.py +51 -0
- context/manager.py +357 -0
- context/project_context.py +245 -0
- context/workspace.py +260 -0
- hooks/__init__.py +1 -0
- hooks/hook_system.py +139 -0
- kryth-0.0.36.dist-info/METADATA +219 -0
- kryth-0.0.36.dist-info/RECORD +132 -0
- kryth-0.0.36.dist-info/WHEEL +5 -0
- kryth-0.0.36.dist-info/entry_points.txt +2 -0
- kryth-0.0.36.dist-info/licenses/LICENSE +674 -0
- kryth-0.0.36.dist-info/top_level.txt +11 -0
- main.py +454 -0
- prompts/__init__.py +31 -0
- prompts/agents_md.py +14 -0
- prompts/compaction.py +27 -0
- prompts/environment.py +44 -0
- prompts/identity.py +13 -0
- prompts/instructions.py +33 -0
- prompts/loop_breaker.py +15 -0
- prompts/operational.py +77 -0
- prompts/security.py +15 -0
- prompts/skills.py +30 -0
- prompts/system.py +63 -0
- prompts/tool_guidelines.py +71 -0
- safety/__init__.py +1 -0
- safety/approval.py +245 -0
- tools/__init__.py +26 -0
- tools/base.py +217 -0
- tools/core/__init__.py +70 -0
- tools/core/apply_patch.py +645 -0
- tools/core/bash.py +344 -0
- tools/core/code_integrity.py +258 -0
- tools/core/dependency_validator.py +321 -0
- tools/core/directories.py +65 -0
- tools/core/edit.py +245 -0
- tools/core/error_recovery.py +474 -0
- tools/core/glob.py +92 -0
- tools/core/grep.py +128 -0
- tools/core/long_running.py +174 -0
- tools/core/persistent_terminal.py +454 -0
- tools/core/plan.py +145 -0
- tools/core/project.py +550 -0
- tools/core/read.py +126 -0
- tools/core/shells.py +867 -0
- tools/core/terminal.py +382 -0
- tools/core/tool_supervisor.py +337 -0
- tools/core/write.py +135 -0
- tools/discovery.py +89 -0
- tools/mcp/client.py +127 -0
- tools/mcp/manager.py +80 -0
- tools/mcp/mcp_tool.py +54 -0
- tools/memory/__init__.py +0 -0
- tools/memory/memory.py +189 -0
- tools/network/__init__.py +0 -0
- tools/network/fetch.py +74 -0
- tools/network/headers.py +14 -0
- tools/network/search.py +68 -0
- tools/registry.py +189 -0
- tools/subagents/subagents.py +318 -0
- ui/__init__.py +6 -0
- ui/components/__init__.py +112 -0
- ui/components/args_table.py +14 -0
- ui/components/confirmation.py +86 -0
- ui/components/diff_viewer.py +37 -0
- ui/components/gutter.py +26 -0
- ui/components/logo.py +91 -0
- ui/components/markdown.py +201 -0
- ui/components/memory.py +29 -0
- ui/components/notification.py +26 -0
- ui/components/permission_prompt.py +59 -0
- ui/components/plan.py +35 -0
- ui/components/shimmer.py +38 -0
- ui/components/spinner.py +84 -0
- ui/components/status_bar.py +47 -0
- ui/components/terminal_approval.py +367 -0
- ui/components/thinking.py +47 -0
- ui/components/tool_call.py +458 -0
- ui/components/tool_progress.py +33 -0
- ui/components/transcript.py +70 -0
- ui/components/usage.py +29 -0
- ui/components/welcome.py +150 -0
- ui/console.py +12 -0
- ui/format/__init__.py +33 -0
- ui/format/args.py +80 -0
- ui/format/code.py +63 -0
- ui/format/diff.py +50 -0
- ui/format/elapsed.py +32 -0
- ui/motion.py +16 -0
- ui/repl/__init__.py +6 -0
- ui/repl/app.py +292 -0
- ui/repl/banner.py +52 -0
- ui/repl/commands/__init__.py +17 -0
- ui/repl/commands/base.py +40 -0
- ui/repl/commands/dispatch.py +217 -0
- ui/repl/commands/help.py +47 -0
- ui/repl/commands/inspect.py +80 -0
- ui/repl/commands/sessions.py +376 -0
- ui/repl/commands/settings.py +140 -0
- ui/repl/completer.py +37 -0
- ui/repl/keys.py +177 -0
- ui/repl/prompt.py +130 -0
- ui/setup_tui.py +435 -0
- ui/stream.py +95 -0
- ui/theme/__init__.py +24 -0
- ui/theme/palette.py +50 -0
- ui/theme/styles.py +56 -0
- ui/theme/syntax.py +69 -0
- ui/tui.py +649 -0
- utils/__init__.py +17 -0
- utils/development_supervisor.py +160 -0
- utils/errors.py +47 -0
- utils/paths.py +184 -0
- utils/text.py +74 -0
agent/__init__.py
ADDED
agent/agent.py
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import json
|
|
3
|
+
from typing import AsyncGenerator, Awaitable, Callable
|
|
4
|
+
from agent.session import Session
|
|
5
|
+
from agent.store import SessionRecord
|
|
6
|
+
from config.config import Config
|
|
7
|
+
from agent.events import AgentEvent, AgentEventType
|
|
8
|
+
from client.response import StreamEventType, TokenUsage, ToolCall, ToolResultMessage
|
|
9
|
+
from prompts import create_loop_breaker_prompt
|
|
10
|
+
from tools.base import ToolConfirmation
|
|
11
|
+
|
|
12
|
+
class Agent:
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
config: Config,
|
|
16
|
+
confirmation_callback: Callable[[ToolConfirmation], Awaitable[bool]] | None = None,
|
|
17
|
+
resume: str | None = None,
|
|
18
|
+
progress_callback: Callable[[str], None] | None = None,
|
|
19
|
+
):
|
|
20
|
+
self.config = config
|
|
21
|
+
self.session: Session | None = Session(self.config)
|
|
22
|
+
self.session.approval_manager.confirmation_callback = confirmation_callback
|
|
23
|
+
|
|
24
|
+
self.progress_callback = progress_callback
|
|
25
|
+
|
|
26
|
+
# Resolved on entry, once the session has a context manager to fill.
|
|
27
|
+
self._resume = resume
|
|
28
|
+
self.resumed: SessionRecord | None = None
|
|
29
|
+
|
|
30
|
+
async def run(self, message: str):
|
|
31
|
+
await self.session.hook_system.trigger_before_agent(message)
|
|
32
|
+
|
|
33
|
+
# PHASE 1 — Workspace ambiguity check. An empty/ambiguous workspace does
|
|
34
|
+
# not automatically mean the user wants a new project. Ask for
|
|
35
|
+
# clarification instead of silently creating nested project directories.
|
|
36
|
+
if self.session.project_context and self.session.project_context.is_ambiguous:
|
|
37
|
+
hint = (
|
|
38
|
+
self.session.project_context_hint
|
|
39
|
+
or f"Workspace `{self.session.config.cwd}` appears to be empty or new."
|
|
40
|
+
)
|
|
41
|
+
yield AgentEvent.agent_error(
|
|
42
|
+
"Clarification needed: "
|
|
43
|
+
+ hint
|
|
44
|
+
+ "\n\nDid you mean to start a new project here, or point KRYTH at a "
|
|
45
|
+
"different directory?",
|
|
46
|
+
details={
|
|
47
|
+
"ambiguous_workspace": True,
|
|
48
|
+
"workspace": str(self.session.config.cwd),
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
yield AgentEvent.agent_start(message)
|
|
54
|
+
self.session.inc_turn()
|
|
55
|
+
self.session.note_prompt(message)
|
|
56
|
+
self.session.reset_turn_usage()
|
|
57
|
+
self.session.context_manager.add_user_message(message)
|
|
58
|
+
|
|
59
|
+
final_response: str | None = None
|
|
60
|
+
|
|
61
|
+
async for event in self._agentic_loop():
|
|
62
|
+
yield event
|
|
63
|
+
|
|
64
|
+
if event.type == AgentEventType.TEXT_COMPLETE:
|
|
65
|
+
final_response = event.data.get("content")
|
|
66
|
+
|
|
67
|
+
await self.session.hook_system.trigger_after_agent(message, final_response)
|
|
68
|
+
self.session.save_checkpoint()
|
|
69
|
+
yield AgentEvent.agent_end(final_response, self.session.last_usage)
|
|
70
|
+
|
|
71
|
+
async def _agentic_loop(self) -> AsyncGenerator[AgentEvent, None]:
|
|
72
|
+
|
|
73
|
+
max_turns = self.config.max_turns
|
|
74
|
+
|
|
75
|
+
for i in range(max_turns):
|
|
76
|
+
|
|
77
|
+
response_text = ""
|
|
78
|
+
reasoning_text = ""
|
|
79
|
+
reasoning_done = False
|
|
80
|
+
|
|
81
|
+
# Prune before compacting: a tool-heavy turn can blow past the context
|
|
82
|
+
# window without ever reaching the end of the loop.
|
|
83
|
+
self.session.context_manager.prune_tool_outputs()
|
|
84
|
+
|
|
85
|
+
if self.session.context_manager.needs_compression():
|
|
86
|
+
summary, usage = await self.session.chat_compactor.compress(
|
|
87
|
+
self.session.context_manager
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if summary:
|
|
91
|
+
self.session.context_manager.replace_with_summary(summary)
|
|
92
|
+
self.session.context_manager.set_latest_usage(usage)
|
|
93
|
+
self.session.context_manager.add_usage(usage)
|
|
94
|
+
|
|
95
|
+
tool_schemas = self.session.tool_registry.get_schemas()
|
|
96
|
+
|
|
97
|
+
tool_calls: list[ToolCall] = []
|
|
98
|
+
usage: TokenUsage | None = None
|
|
99
|
+
|
|
100
|
+
async for event in self.session.client.chat_completion(
|
|
101
|
+
self.session.context_manager.get_messages(),
|
|
102
|
+
tools=tool_schemas if tool_schemas else None,
|
|
103
|
+
stream=True
|
|
104
|
+
):
|
|
105
|
+
if event.type == StreamEventType.REASONING_DELTA:
|
|
106
|
+
if not event.reasoning_delta:
|
|
107
|
+
continue
|
|
108
|
+
reasoning_text += event.reasoning_delta
|
|
109
|
+
yield AgentEvent.reasoning_delta(event.reasoning_delta)
|
|
110
|
+
elif event.type == StreamEventType.TEXT_DELTA:
|
|
111
|
+
if not event.text_delta:
|
|
112
|
+
continue
|
|
113
|
+
content = event.text_delta.content
|
|
114
|
+
# Thinking always ends where the answer begins.
|
|
115
|
+
if reasoning_text and not reasoning_done:
|
|
116
|
+
reasoning_done = True
|
|
117
|
+
yield AgentEvent.reasoning_complete(reasoning_text)
|
|
118
|
+
response_text += content
|
|
119
|
+
yield AgentEvent.text_delta(content)
|
|
120
|
+
elif event.type == StreamEventType.TOOL_CALL_COMPLETE:
|
|
121
|
+
if event.tool_call:
|
|
122
|
+
tool_calls.append(event.tool_call)
|
|
123
|
+
elif event.type == StreamEventType.MESSAGE_COMPLETE:
|
|
124
|
+
if event.reasoning_delta and not reasoning_text:
|
|
125
|
+
reasoning_text = event.reasoning_delta
|
|
126
|
+
yield AgentEvent.reasoning_delta(event.reasoning_delta)
|
|
127
|
+
if event.usage:
|
|
128
|
+
usage = event.usage
|
|
129
|
+
self.session.last_usage = event.usage
|
|
130
|
+
self.session.turn_usage += event.usage
|
|
131
|
+
yield AgentEvent.usage(self.session.turn_usage)
|
|
132
|
+
if event.text_delta:
|
|
133
|
+
if reasoning_text and not reasoning_done:
|
|
134
|
+
reasoning_done = True
|
|
135
|
+
yield AgentEvent.reasoning_complete(reasoning_text)
|
|
136
|
+
response_text += event.text_delta.content
|
|
137
|
+
yield AgentEvent.text_delta(event.text_delta.content)
|
|
138
|
+
if event.tool_calls:
|
|
139
|
+
tool_calls.extend(event.tool_calls)
|
|
140
|
+
elif event.type == StreamEventType.ERROR:
|
|
141
|
+
if reasoning_text and not reasoning_done:
|
|
142
|
+
reasoning_done = True
|
|
143
|
+
yield AgentEvent.reasoning_complete(reasoning_text)
|
|
144
|
+
yield AgentEvent.agent_error(event.error or "Unknown error occurred.")
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
# Reasoning with no answer after it: the model went straight to
|
|
148
|
+
# tool calls, so nothing else will close the block.
|
|
149
|
+
if reasoning_text and not reasoning_done:
|
|
150
|
+
yield AgentEvent.reasoning_complete(reasoning_text)
|
|
151
|
+
|
|
152
|
+
self.session.context_manager.add_assistant_message(
|
|
153
|
+
response_text or None,
|
|
154
|
+
[
|
|
155
|
+
{
|
|
156
|
+
'id': tc.call_id,
|
|
157
|
+
'type': 'function',
|
|
158
|
+
'function': {
|
|
159
|
+
'name': tc.name,
|
|
160
|
+
'arguments': json.dumps(tc.arguments),
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
for tc in tool_calls
|
|
164
|
+
]
|
|
165
|
+
if tool_calls
|
|
166
|
+
else None
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
if response_text:
|
|
170
|
+
yield AgentEvent.text_complete(response_text)
|
|
171
|
+
self.session.loop_detector.record_action(
|
|
172
|
+
'response',
|
|
173
|
+
text=response_text
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if not tool_calls:
|
|
177
|
+
if usage:
|
|
178
|
+
self.session.context_manager.set_latest_usage(usage)
|
|
179
|
+
self.session.context_manager.add_usage(usage)
|
|
180
|
+
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
tool_call_results: list[ToolResultMessage] = []
|
|
184
|
+
|
|
185
|
+
for tool_call in tool_calls:
|
|
186
|
+
|
|
187
|
+
yield AgentEvent.tool_call_start(
|
|
188
|
+
tool_call.call_id,
|
|
189
|
+
tool_call.name,
|
|
190
|
+
tool_call.arguments
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
self.session.loop_detector.record_action(
|
|
194
|
+
"tool_call",
|
|
195
|
+
tool_name=tool_call.name,
|
|
196
|
+
args=tool_call.arguments
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
result = await self.session.tool_registry.invoke(
|
|
200
|
+
tool_call.name,
|
|
201
|
+
tool_call.arguments,
|
|
202
|
+
self.config.cwd,
|
|
203
|
+
self.session.hook_system,
|
|
204
|
+
self.session.approval_manager,
|
|
205
|
+
progress_callback=self.progress_callback,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
yield AgentEvent.tool_call_complete(
|
|
209
|
+
tool_call.call_id,
|
|
210
|
+
tool_call.name,
|
|
211
|
+
result,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
tool_call_results.append(
|
|
215
|
+
ToolResultMessage(
|
|
216
|
+
tool_call_id=tool_call.call_id,
|
|
217
|
+
content=result.to_model_output(),
|
|
218
|
+
is_error=not result.success
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
for tool_result in tool_call_results:
|
|
223
|
+
self.session.context_manager.add_tool_result(
|
|
224
|
+
tool_result.tool_call_id,
|
|
225
|
+
tool_result.content,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
loop_detection_err = self.session.loop_detector.check_for_loop()
|
|
229
|
+
|
|
230
|
+
if loop_detection_err:
|
|
231
|
+
|
|
232
|
+
loop_prompt = create_loop_breaker_prompt(loop_detection_err)
|
|
233
|
+
|
|
234
|
+
self.session.context_manager.add_user_message(loop_prompt)
|
|
235
|
+
continue
|
|
236
|
+
|
|
237
|
+
async def __aenter__(self) -> Agent:
|
|
238
|
+
await self.session.initalize(progress=self.progress_callback)
|
|
239
|
+
if self._resume:
|
|
240
|
+
self.resumed = self.session.resume(self._resume)
|
|
241
|
+
return self
|
|
242
|
+
|
|
243
|
+
async def __aexit__(
|
|
244
|
+
self,
|
|
245
|
+
exc_type,
|
|
246
|
+
exc_val,
|
|
247
|
+
exc_tb
|
|
248
|
+
) -> None:
|
|
249
|
+
|
|
250
|
+
if self.session and self.session.client and self.session.mcp_manager:
|
|
251
|
+
await self.session.client.close()
|
|
252
|
+
await self.session.mcp_manager.shutdown()
|
|
253
|
+
self.session = None # Close session, everything else will be garbage collected
|
agent/events.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Any
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from client.response import TokenUsage
|
|
6
|
+
from tools.base import ToolResult
|
|
7
|
+
|
|
8
|
+
class AgentEventType(str, Enum):
|
|
9
|
+
|
|
10
|
+
# Agentic loops, and err
|
|
11
|
+
AGENT_START="agent_start"
|
|
12
|
+
AGENT_END="agent_end"
|
|
13
|
+
AGENT_ERROR="agent_error"
|
|
14
|
+
|
|
15
|
+
# Token accounting, emitted once per completion
|
|
16
|
+
USAGE = "usage"
|
|
17
|
+
|
|
18
|
+
# Text streaming
|
|
19
|
+
TEXT_DELTA = "text_delta"
|
|
20
|
+
TEXT_COMPLETE = "text_complete"
|
|
21
|
+
|
|
22
|
+
# Reasoning ("thinking") streaming, only for models that expose it
|
|
23
|
+
REASONING_DELTA = "reasoning_delta"
|
|
24
|
+
REASONING_COMPLETE = "reasoning_complete"
|
|
25
|
+
|
|
26
|
+
# Tool Calls
|
|
27
|
+
TOOL_CALL_START = 'tool_call_start'
|
|
28
|
+
TOOL_CALL_COMPLETE = 'tool_call_complete'
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class AgentEvent:
|
|
32
|
+
type: AgentEventType
|
|
33
|
+
data: dict[str, Any] = field(default_factory=dict)
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def agent_start(
|
|
37
|
+
cls,
|
|
38
|
+
message: str
|
|
39
|
+
) -> AgentEvent:
|
|
40
|
+
return cls(
|
|
41
|
+
type=AgentEventType.AGENT_START,
|
|
42
|
+
data={
|
|
43
|
+
"message": message
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def agent_end(
|
|
49
|
+
cls,
|
|
50
|
+
response: str | None = None,
|
|
51
|
+
usage: TokenUsage | None = None
|
|
52
|
+
) -> AgentEvent:
|
|
53
|
+
return cls(
|
|
54
|
+
type=AgentEventType.AGENT_END,
|
|
55
|
+
data={
|
|
56
|
+
"response": response,
|
|
57
|
+
'usage': usage.__dict__ if usage else None
|
|
58
|
+
},
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def usage(
|
|
63
|
+
cls,
|
|
64
|
+
turn_usage: TokenUsage
|
|
65
|
+
) -> AgentEvent:
|
|
66
|
+
return cls(
|
|
67
|
+
type=AgentEventType.USAGE,
|
|
68
|
+
data={
|
|
69
|
+
'usage': turn_usage.__dict__,
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def agent_error(
|
|
75
|
+
cls,
|
|
76
|
+
error: str,
|
|
77
|
+
details: dict[str, Any] | None = None
|
|
78
|
+
) -> AgentEvent:
|
|
79
|
+
return cls(
|
|
80
|
+
type=AgentEventType.AGENT_ERROR,
|
|
81
|
+
data={
|
|
82
|
+
"error": error,
|
|
83
|
+
'details': details or {}
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def text_delta(
|
|
89
|
+
cls,
|
|
90
|
+
content: str
|
|
91
|
+
) -> AgentEvent:
|
|
92
|
+
return cls(
|
|
93
|
+
type=AgentEventType.TEXT_DELTA,
|
|
94
|
+
data={
|
|
95
|
+
"content": content
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def text_complete(
|
|
101
|
+
cls,
|
|
102
|
+
content: str
|
|
103
|
+
) -> AgentEvent:
|
|
104
|
+
return cls(
|
|
105
|
+
type=AgentEventType.TEXT_COMPLETE,
|
|
106
|
+
data={
|
|
107
|
+
"content": content
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
@classmethod
|
|
112
|
+
def reasoning_delta(
|
|
113
|
+
cls,
|
|
114
|
+
content: str
|
|
115
|
+
) -> AgentEvent:
|
|
116
|
+
return cls(
|
|
117
|
+
type=AgentEventType.REASONING_DELTA,
|
|
118
|
+
data={
|
|
119
|
+
"content": content
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def reasoning_complete(
|
|
125
|
+
cls,
|
|
126
|
+
content: str
|
|
127
|
+
) -> AgentEvent:
|
|
128
|
+
return cls(
|
|
129
|
+
type=AgentEventType.REASONING_COMPLETE,
|
|
130
|
+
data={
|
|
131
|
+
"content": content
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def tool_call_start(cls, call_id: str, name: str, arguments: dict[str, Any]):
|
|
137
|
+
return cls(
|
|
138
|
+
type=AgentEventType.TOOL_CALL_START,
|
|
139
|
+
data={
|
|
140
|
+
'call_id': call_id,
|
|
141
|
+
'name': name,
|
|
142
|
+
'arguments': arguments
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
def tool_call_complete(cls, call_id: str, name: str, result: ToolResult):
|
|
148
|
+
return cls(
|
|
149
|
+
type=AgentEventType.TOOL_CALL_COMPLETE,
|
|
150
|
+
data={
|
|
151
|
+
'call_id': call_id,
|
|
152
|
+
'name': name,
|
|
153
|
+
'success': result.success,
|
|
154
|
+
'output': result.output,
|
|
155
|
+
'error': result.error,
|
|
156
|
+
'metadata': result.metadata,
|
|
157
|
+
'diff': result.diff.create_diff() if result.diff else None,
|
|
158
|
+
'truncated': result.truncated,
|
|
159
|
+
'exit_code': result.exit_code,
|
|
160
|
+
}
|
|
161
|
+
)
|