tunacode-cli 0.0.72__py3-none-any.whl → 0.0.75__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.
Potentially problematic release.
This version of tunacode-cli might be problematic. Click here for more details.
- tunacode/cli/repl.py +2 -1
- tunacode/constants.py +1 -1
- tunacode/core/agents/agent_components/agent_config.py +7 -4
- tunacode/core/agents/agent_components/agent_helpers.py +1 -1
- tunacode/core/agents/agent_components/node_processor.py +35 -3
- tunacode/core/agents/agent_components/response_state.py +109 -6
- tunacode/core/agents/agent_components/state_transition.py +116 -0
- tunacode/core/agents/agent_components/task_completion.py +10 -6
- tunacode/core/agents/main.py +4 -4
- tunacode/prompts/system.md +11 -1
- tunacode/types.py +9 -0
- {tunacode_cli-0.0.72.dist-info → tunacode_cli-0.0.75.dist-info}/METADATA +1 -1
- {tunacode_cli-0.0.72.dist-info → tunacode_cli-0.0.75.dist-info}/RECORD +16 -15
- {tunacode_cli-0.0.72.dist-info → tunacode_cli-0.0.75.dist-info}/WHEEL +0 -0
- {tunacode_cli-0.0.72.dist-info → tunacode_cli-0.0.75.dist-info}/entry_points.txt +0 -0
- {tunacode_cli-0.0.72.dist-info → tunacode_cli-0.0.75.dist-info}/licenses/LICENSE +0 -0
tunacode/cli/repl.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
6
|
+
import re
|
|
6
7
|
import subprocess
|
|
7
8
|
from asyncio.exceptions import CancelledError
|
|
8
9
|
from pathlib import Path
|
|
@@ -112,7 +113,7 @@ async def _detect_and_handle_text_plan(state_manager, agent_response, original_r
|
|
|
112
113
|
else:
|
|
113
114
|
response_text = str(agent_response)
|
|
114
115
|
|
|
115
|
-
if "
|
|
116
|
+
if re.search(r"^\s*TUNACODE\s+DONE:\s*", response_text, re.IGNORECASE):
|
|
116
117
|
await ui.warning(
|
|
117
118
|
"⚠️ Agent failed to call present_plan tool. Please provide clearer instructions."
|
|
118
119
|
)
|
tunacode/constants.py
CHANGED
|
@@ -169,18 +169,21 @@ def get_or_create_agent(model: ModelName, state_manager: StateManager) -> Pydant
|
|
|
169
169
|
|
|
170
170
|
# Add plan mode context if in plan mode
|
|
171
171
|
if state_manager.is_plan_mode():
|
|
172
|
-
# REMOVE
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
)
|
|
172
|
+
# REMOVE completion instructions from the system prompt in plan mode
|
|
173
|
+
for marker in ("TUNACODE_TASK_COMPLETE", "TUNACODE DONE:"):
|
|
174
|
+
system_prompt = system_prompt.replace(marker, "PLAN_MODE_TASK_PLACEHOLDER")
|
|
176
175
|
# Remove the completion guidance that conflicts with plan mode
|
|
177
176
|
lines_to_remove = [
|
|
177
|
+
"When a task is COMPLETE, start your response with: TUNACODE DONE:",
|
|
178
|
+
"4. When a task is COMPLETE, start your response with: TUNACODE DONE:",
|
|
178
179
|
"When a task is COMPLETE, start your response with: TUNACODE_TASK_COMPLETE",
|
|
179
180
|
"4. When a task is COMPLETE, start your response with: TUNACODE_TASK_COMPLETE",
|
|
180
181
|
"**How to signal completion:**",
|
|
181
182
|
"TUNACODE_TASK_COMPLETE",
|
|
183
|
+
"TUNACODE DONE:",
|
|
182
184
|
"[Your summary of what was accomplished]",
|
|
183
185
|
"**IMPORTANT**: Always evaluate if you've completed the task. If yes, use TUNACODE_TASK_COMPLETE.",
|
|
186
|
+
"**IMPORTANT**: Always evaluate if you've completed the task. If yes, use TUNACODE DONE:",
|
|
184
187
|
"This prevents wasting iterations and API calls.",
|
|
185
188
|
]
|
|
186
189
|
for line in lines_to_remove:
|
|
@@ -106,7 +106,7 @@ Attempt: {iteration}
|
|
|
106
106
|
Please take one of these specific actions:
|
|
107
107
|
|
|
108
108
|
1. **Search yielded no results?** → Try alternative search terms or broader patterns
|
|
109
|
-
2. **Found what you need?** → Use
|
|
109
|
+
2. **Found what you need?** → Use TUNACODE DONE: to finalize
|
|
110
110
|
3. **Encountering a blocker?** → Explain the specific issue preventing progress
|
|
111
111
|
4. **Need more context?** → Use list_dir or expand your search scope
|
|
112
112
|
|
|
@@ -5,7 +5,7 @@ from typing import Any, Awaitable, Callable, Optional, Tuple
|
|
|
5
5
|
|
|
6
6
|
from tunacode.core.logging.logger import get_logger
|
|
7
7
|
from tunacode.core.state import StateManager
|
|
8
|
-
from tunacode.types import UsageTrackerProtocol
|
|
8
|
+
from tunacode.types import AgentState, UsageTrackerProtocol
|
|
9
9
|
from tunacode.ui.tool_descriptions import get_batch_description, get_tool_description
|
|
10
10
|
|
|
11
11
|
from .response_state import ResponseState
|
|
@@ -52,6 +52,12 @@ async def _process_node(
|
|
|
52
52
|
has_intention = False
|
|
53
53
|
has_tool_calls = False
|
|
54
54
|
|
|
55
|
+
# Transition to ASSISTANT at the start of node processing
|
|
56
|
+
if response_state and response_state.can_transition_to(AgentState.ASSISTANT):
|
|
57
|
+
response_state.transition_to(AgentState.ASSISTANT)
|
|
58
|
+
if state_manager.session.show_thoughts:
|
|
59
|
+
await ui.muted("STATE → ASSISTANT (reasoning)")
|
|
60
|
+
|
|
55
61
|
if hasattr(node, "request"):
|
|
56
62
|
state_manager.session.messages.append(node.request)
|
|
57
63
|
|
|
@@ -161,8 +167,11 @@ async def _process_node(
|
|
|
161
167
|
f"Task completion with pending intentions detected: {found_phrases}"
|
|
162
168
|
)
|
|
163
169
|
|
|
164
|
-
# Normal completion
|
|
165
|
-
response_state.
|
|
170
|
+
# Normal completion - transition to RESPONSE state and mark completion
|
|
171
|
+
response_state.transition_to(AgentState.RESPONSE)
|
|
172
|
+
if state_manager.session.show_thoughts:
|
|
173
|
+
await ui.muted("STATE → RESPONSE (completion detected)")
|
|
174
|
+
response_state.set_completion_detected(True)
|
|
166
175
|
response_state.has_user_response = True
|
|
167
176
|
# Update the part content to remove the marker
|
|
168
177
|
part.content = cleaned_content
|
|
@@ -217,6 +226,14 @@ async def _process_node(
|
|
|
217
226
|
node, buffering_callback, state_manager, tool_buffer, response_state
|
|
218
227
|
)
|
|
219
228
|
|
|
229
|
+
# If there were no tools and we processed a model response, transition to RESPONSE
|
|
230
|
+
if response_state and response_state.can_transition_to(AgentState.RESPONSE):
|
|
231
|
+
# Only transition if not already completed (set by completion marker path)
|
|
232
|
+
if not response_state.is_completed():
|
|
233
|
+
response_state.transition_to(AgentState.RESPONSE)
|
|
234
|
+
if state_manager.session.show_thoughts:
|
|
235
|
+
await ui.muted("STATE → RESPONSE (handled output)")
|
|
236
|
+
|
|
220
237
|
# Determine empty response reason
|
|
221
238
|
if empty_response_detected:
|
|
222
239
|
if appears_truncated:
|
|
@@ -306,6 +323,11 @@ async def _process_tool_calls(
|
|
|
306
323
|
for part in node.model_response.parts:
|
|
307
324
|
if hasattr(part, "part_kind") and part.part_kind == "tool-call":
|
|
308
325
|
is_processing_tools = True
|
|
326
|
+
# Transition to TOOL_EXECUTION on first tool call
|
|
327
|
+
if response_state and response_state.can_transition_to(AgentState.TOOL_EXECUTION):
|
|
328
|
+
response_state.transition_to(AgentState.TOOL_EXECUTION)
|
|
329
|
+
if state_manager.session.show_thoughts:
|
|
330
|
+
await ui.muted("STATE → TOOL_EXECUTION (executing tools)")
|
|
309
331
|
if tool_callback:
|
|
310
332
|
# Check if this is a read-only tool that can be batched
|
|
311
333
|
if tool_buffer is not None and part.tool_name in READ_ONLY_TOOLS:
|
|
@@ -440,6 +462,16 @@ async def _process_tool_calls(
|
|
|
440
462
|
}
|
|
441
463
|
state_manager.session.tool_calls.append(tool_info)
|
|
442
464
|
|
|
465
|
+
# After tools are processed, transition back to RESPONSE
|
|
466
|
+
if (
|
|
467
|
+
is_processing_tools
|
|
468
|
+
and response_state
|
|
469
|
+
and response_state.can_transition_to(AgentState.RESPONSE)
|
|
470
|
+
):
|
|
471
|
+
response_state.transition_to(AgentState.RESPONSE)
|
|
472
|
+
if state_manager.session.show_thoughts:
|
|
473
|
+
await ui.muted("STATE → RESPONSE (tools finished)")
|
|
474
|
+
|
|
443
475
|
# Update has_user_response based on presence of actual response content
|
|
444
476
|
if (
|
|
445
477
|
response_state
|
|
@@ -1,13 +1,116 @@
|
|
|
1
1
|
"""Response state management for tracking agent processing state."""
|
|
2
2
|
|
|
3
|
-
from dataclasses import dataclass
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from tunacode.types import AgentState
|
|
7
|
+
|
|
8
|
+
from .state_transition import AGENT_TRANSITION_RULES, AgentStateMachine
|
|
4
9
|
|
|
5
10
|
|
|
6
11
|
@dataclass
|
|
7
12
|
class ResponseState:
|
|
8
|
-
"""
|
|
13
|
+
"""Enhanced response state using enum-based state machine."""
|
|
14
|
+
|
|
15
|
+
# Internal state machine
|
|
16
|
+
_state_machine: AgentStateMachine = field(
|
|
17
|
+
default_factory=lambda: AgentStateMachine(AgentState.USER_INPUT, AGENT_TRANSITION_RULES)
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Backward compatibility boolean flags (derived from enum state)
|
|
21
|
+
_has_user_response: bool = False
|
|
22
|
+
_task_completed: bool = False
|
|
23
|
+
_awaiting_user_guidance: bool = False
|
|
24
|
+
_has_final_synthesis: bool = False
|
|
25
|
+
|
|
26
|
+
def __post_init__(self):
|
|
27
|
+
"""Initialize the state machine."""
|
|
28
|
+
if not hasattr(self, "_state_machine"):
|
|
29
|
+
self._state_machine = AgentStateMachine(AgentState.USER_INPUT, AGENT_TRANSITION_RULES)
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def current_state(self) -> AgentState:
|
|
33
|
+
"""Get the current enum state."""
|
|
34
|
+
return self._state_machine.current_state
|
|
35
|
+
|
|
36
|
+
def transition_to(self, new_state: AgentState) -> None:
|
|
37
|
+
"""Transition to a new state."""
|
|
38
|
+
self._state_machine.transition_to(new_state)
|
|
39
|
+
|
|
40
|
+
def can_transition_to(self, target_state: AgentState) -> bool:
|
|
41
|
+
"""Check if a transition to the target state is allowed."""
|
|
42
|
+
return self._state_machine.can_transition_to(target_state)
|
|
43
|
+
|
|
44
|
+
# Backward compatibility properties
|
|
45
|
+
@property
|
|
46
|
+
def has_user_response(self) -> bool:
|
|
47
|
+
"""Legacy boolean flag for user response detection."""
|
|
48
|
+
return self._has_user_response
|
|
49
|
+
|
|
50
|
+
@has_user_response.setter
|
|
51
|
+
def has_user_response(self, value: bool) -> None:
|
|
52
|
+
"""Set the legacy has_user_response flag."""
|
|
53
|
+
self._has_user_response = value
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def task_completed(self) -> bool:
|
|
57
|
+
"""Legacy boolean flag for task completion (derived from state machine)."""
|
|
58
|
+
# If explicitly set true, honor it; otherwise derive from state machine
|
|
59
|
+
return bool(self._task_completed or self._state_machine.is_completed())
|
|
60
|
+
|
|
61
|
+
@task_completed.setter
|
|
62
|
+
def task_completed(self, value: bool) -> None:
|
|
63
|
+
"""Set the legacy task_completed flag and sync with state machine."""
|
|
64
|
+
self._task_completed = bool(value)
|
|
65
|
+
if value:
|
|
66
|
+
# Ensure state reflects completion in RESPONSE
|
|
67
|
+
try:
|
|
68
|
+
if (
|
|
69
|
+
self._state_machine.current_state != AgentState.RESPONSE
|
|
70
|
+
and self._state_machine.can_transition_to(AgentState.RESPONSE)
|
|
71
|
+
):
|
|
72
|
+
self._state_machine.transition_to(AgentState.RESPONSE)
|
|
73
|
+
except Exception:
|
|
74
|
+
# Best-effort: ignore invalid transition in legacy paths
|
|
75
|
+
pass
|
|
76
|
+
self._state_machine.set_completion_detected(True)
|
|
77
|
+
else:
|
|
78
|
+
self._state_machine.set_completion_detected(False)
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def awaiting_user_guidance(self) -> bool:
|
|
82
|
+
"""Legacy boolean flag for awaiting user guidance."""
|
|
83
|
+
return self._awaiting_user_guidance
|
|
84
|
+
|
|
85
|
+
@awaiting_user_guidance.setter
|
|
86
|
+
def awaiting_user_guidance(self, value: bool) -> None:
|
|
87
|
+
"""Set the legacy awaiting_user_guidance flag."""
|
|
88
|
+
self._awaiting_user_guidance = value
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def has_final_synthesis(self) -> bool:
|
|
92
|
+
"""Legacy boolean flag for final synthesis."""
|
|
93
|
+
return self._has_final_synthesis
|
|
94
|
+
|
|
95
|
+
@has_final_synthesis.setter
|
|
96
|
+
def has_final_synthesis(self, value: bool) -> None:
|
|
97
|
+
"""Set the legacy has_final_synthesis flag."""
|
|
98
|
+
self._has_final_synthesis = value
|
|
99
|
+
|
|
100
|
+
# Enhanced state management methods
|
|
101
|
+
def set_completion_detected(self, detected: bool = True) -> None:
|
|
102
|
+
"""Mark that completion has been detected in the RESPONSE state."""
|
|
103
|
+
self._state_machine.set_completion_detected(detected)
|
|
104
|
+
|
|
105
|
+
def is_completed(self) -> bool:
|
|
106
|
+
"""Check if the task is completed according to the state machine."""
|
|
107
|
+
return self._state_machine.is_completed()
|
|
9
108
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
109
|
+
def reset_state(self, initial_state: Optional[AgentState] = None) -> None:
|
|
110
|
+
"""Reset the state machine to initial state."""
|
|
111
|
+
self._state_machine.reset(initial_state)
|
|
112
|
+
# Reset legacy flags
|
|
113
|
+
self._has_user_response = False
|
|
114
|
+
self._task_completed = False
|
|
115
|
+
self._awaiting_user_guidance = False
|
|
116
|
+
self._has_final_synthesis = False
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""State transition management for agent response processing."""
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import TYPE_CHECKING, Dict, Set
|
|
7
|
+
|
|
8
|
+
from tunacode.types import AgentState
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InvalidStateTransitionError(Exception):
|
|
15
|
+
"""Raised when an invalid state transition is attempted."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, from_state: Enum, to_state: Enum, message: str = None):
|
|
18
|
+
self.from_state = from_state
|
|
19
|
+
self.to_state = to_state
|
|
20
|
+
self.message = message or f"Invalid state transition: {from_state.value} → {to_state.value}"
|
|
21
|
+
super().__init__(self.message)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class StateTransitionRules:
|
|
26
|
+
"""Defines valid state transitions for the agent state machine."""
|
|
27
|
+
|
|
28
|
+
# Valid transitions for each state
|
|
29
|
+
valid_transitions: Dict[Enum, Set[Enum]]
|
|
30
|
+
|
|
31
|
+
def is_valid_transition(self, from_state: Enum, to_state: Enum) -> bool:
|
|
32
|
+
"""Check if a transition between states is valid."""
|
|
33
|
+
return to_state in self.valid_transitions.get(from_state, set())
|
|
34
|
+
|
|
35
|
+
def get_valid_next_states(self, current_state: Enum) -> Set[Enum]:
|
|
36
|
+
"""Get all valid next states from the current state."""
|
|
37
|
+
return self.valid_transitions.get(current_state, set())
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AgentStateMachine:
|
|
41
|
+
"""Thread-safe state machine for agent response processing."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, initial_state: "AgentState", rules: StateTransitionRules):
|
|
44
|
+
"""
|
|
45
|
+
Initialize the state machine.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
initial_state: The starting state
|
|
49
|
+
rules: Transition rules defining valid state changes
|
|
50
|
+
"""
|
|
51
|
+
self._state = initial_state
|
|
52
|
+
self._rules = rules
|
|
53
|
+
self._lock = threading.RLock() # Reentrant lock for thread safety
|
|
54
|
+
self._completion_detected = False
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def current_state(self) -> "AgentState":
|
|
58
|
+
"""Get the current state."""
|
|
59
|
+
with self._lock:
|
|
60
|
+
return self._state
|
|
61
|
+
|
|
62
|
+
def transition_to(self, new_state: "AgentState") -> None:
|
|
63
|
+
"""
|
|
64
|
+
Transition to a new state.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
new_state: The state to transition to
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
InvalidStateTransitionError: If the transition is not valid
|
|
71
|
+
"""
|
|
72
|
+
with self._lock:
|
|
73
|
+
if not self._rules.is_valid_transition(self._state, new_state):
|
|
74
|
+
raise InvalidStateTransitionError(
|
|
75
|
+
self._state,
|
|
76
|
+
new_state,
|
|
77
|
+
f"Invalid state transition: {self._state.value} → {new_state.value}",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Handle self-transitions as no-ops
|
|
81
|
+
if self._state == new_state:
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
self._state = new_state
|
|
85
|
+
|
|
86
|
+
def can_transition_to(self, target_state: "AgentState") -> bool:
|
|
87
|
+
"""Check if a transition to the target state is allowed."""
|
|
88
|
+
with self._lock:
|
|
89
|
+
return self._rules.is_valid_transition(self._state, target_state)
|
|
90
|
+
|
|
91
|
+
def set_completion_detected(self, detected: bool = True) -> None:
|
|
92
|
+
"""Mark that completion has been detected in the RESPONSE state."""
|
|
93
|
+
with self._lock:
|
|
94
|
+
self._completion_detected = detected
|
|
95
|
+
|
|
96
|
+
def is_completed(self) -> bool:
|
|
97
|
+
"""Check if the task is completed (only valid in RESPONSE state)."""
|
|
98
|
+
with self._lock:
|
|
99
|
+
return self._state == AgentState.RESPONSE and self._completion_detected
|
|
100
|
+
|
|
101
|
+
def reset(self, initial_state: "AgentState" = None) -> None:
|
|
102
|
+
"""Reset the state machine to initial state."""
|
|
103
|
+
with self._lock:
|
|
104
|
+
self._state = initial_state or AgentState.USER_INPUT
|
|
105
|
+
self._completion_detected = False
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# Define the transition rules for the agent state machine
|
|
109
|
+
AGENT_TRANSITION_RULES = StateTransitionRules(
|
|
110
|
+
valid_transitions={
|
|
111
|
+
AgentState.USER_INPUT: {AgentState.ASSISTANT},
|
|
112
|
+
AgentState.ASSISTANT: {AgentState.TOOL_EXECUTION, AgentState.RESPONSE},
|
|
113
|
+
AgentState.TOOL_EXECUTION: {AgentState.RESPONSE},
|
|
114
|
+
AgentState.RESPONSE: {AgentState.ASSISTANT}, # Can transition back to continue
|
|
115
|
+
}
|
|
116
|
+
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Task completion detection utilities."""
|
|
2
2
|
|
|
3
|
+
import re
|
|
3
4
|
from typing import Tuple
|
|
4
5
|
|
|
5
6
|
|
|
@@ -18,11 +19,14 @@ def check_task_completion(content: str) -> Tuple[bool, str]:
|
|
|
18
19
|
if not content:
|
|
19
20
|
return False, content
|
|
20
21
|
|
|
21
|
-
lines = content.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
lines = content.split("\n")
|
|
23
|
+
|
|
24
|
+
# New marker: any line starting with "TUNACODE DONE:" (case-insensitive, allow leading whitespace)
|
|
25
|
+
done_pattern = re.compile(r"^\s*TUNACODE\s+DONE:\s*", re.IGNORECASE)
|
|
26
|
+
for idx, line in enumerate(lines):
|
|
27
|
+
if done_pattern.match(line):
|
|
28
|
+
# Remove the marker line and return remaining content
|
|
29
|
+
cleaned = "\n".join(lines[:idx] + lines[idx + 1 :]).strip()
|
|
30
|
+
return True, cleaned
|
|
27
31
|
|
|
28
32
|
return False, content
|
tunacode/core/agents/main.py
CHANGED
|
@@ -97,7 +97,7 @@ async def check_query_satisfaction(
|
|
|
97
97
|
state_manager: StateManager,
|
|
98
98
|
) -> bool:
|
|
99
99
|
"""Check if the response satisfies the original query."""
|
|
100
|
-
return True #
|
|
100
|
+
return True # Completion decided via DONE marker in RESPONSE
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
async def process_request(
|
|
@@ -251,7 +251,7 @@ Task: {message[:200]}...
|
|
|
251
251
|
|
|
252
252
|
You're describing actions but not executing them. You MUST:
|
|
253
253
|
|
|
254
|
-
1. If task is COMPLETE: Start response with
|
|
254
|
+
1. If task is COMPLETE: Start response with TUNACODE DONE:
|
|
255
255
|
2. If task needs work: Execute a tool RIGHT NOW (grep, read_file, bash, etc.)
|
|
256
256
|
3. If stuck: Explain the specific blocker
|
|
257
257
|
|
|
@@ -269,7 +269,7 @@ NO MORE DESCRIPTIONS. Take ACTION or mark COMPLETE."""
|
|
|
269
269
|
unproductive_iterations = 0
|
|
270
270
|
|
|
271
271
|
# REMOVED: Recursive satisfaction check that caused empty responses
|
|
272
|
-
# The agent now decides completion using
|
|
272
|
+
# The agent now decides completion using a DONE marker
|
|
273
273
|
# This eliminates recursive agent calls and gives control back to the agent
|
|
274
274
|
|
|
275
275
|
# Store original query for reference
|
|
@@ -302,7 +302,7 @@ Progress so far:
|
|
|
302
302
|
- Iterations: {i}
|
|
303
303
|
- Tools used: {tools_used_str}
|
|
304
304
|
|
|
305
|
-
If the task is complete, I should respond with
|
|
305
|
+
If the task is complete, I should respond with TUNACODE DONE:
|
|
306
306
|
Otherwise, please provide specific guidance on what to do next."""
|
|
307
307
|
|
|
308
308
|
create_user_message(clarification_content, state_manager)
|
tunacode/prompts/system.md
CHANGED
|
@@ -10,12 +10,22 @@ CRITICAL BEHAVIOR RULES:
|
|
|
10
10
|
1. ALWAYS ANNOUNCE YOUR INTENTIONS FIRST: Before executing any tools, briefly state what you're about to do (e.g., "I'll search for the main agent implementation" or "Let me examine the file structure")
|
|
11
11
|
2. When you say "Let me..." or "I will..." you MUST execute the corresponding tool in THE SAME RESPONSE
|
|
12
12
|
3. Never describe what you'll do without doing it ALWAYS execute tools when discussing actions
|
|
13
|
-
4. When a task is COMPLETE, start your response with:
|
|
13
|
+
4. When a task is COMPLETE, start your response with: TUNACODE DONE:
|
|
14
14
|
5. If your response is cut off or truncated, you'll be prompted to continue complete your action
|
|
15
15
|
6. YOU MUST NOT USE ANY EMOJIS, YOU WILL BE PUNISHED FOR EMOJI USE
|
|
16
16
|
|
|
17
17
|
You MUST follow these rules:
|
|
18
18
|
|
|
19
|
+
### Completion Signaling
|
|
20
|
+
|
|
21
|
+
When you have fully completed the user’s task:
|
|
22
|
+
|
|
23
|
+
- Start your response with a single line: `TUNACODE DONE:` followed by a brief outcome summary.
|
|
24
|
+
- Do not add explanations before the DONE line; keep it as the first line.
|
|
25
|
+
- Do NOT mark DONE if you have queued tools in the same response — execute tools first, then mark DONE.
|
|
26
|
+
- Example:
|
|
27
|
+
- `TUNACODE DONE: Implemented enum state machine and updated completion logic`
|
|
28
|
+
|
|
19
29
|
###Tool Access Rules###
|
|
20
30
|
|
|
21
31
|
You have 9 powerful tools at your disposal. Understanding their categories is CRITICAL for performance:
|
tunacode/types.py
CHANGED
|
@@ -209,6 +209,15 @@ class PlanPhase(Enum):
|
|
|
209
209
|
REVIEW_DECISION = "review"
|
|
210
210
|
|
|
211
211
|
|
|
212
|
+
class AgentState(Enum):
|
|
213
|
+
"""Agent loop states for enhanced completion detection."""
|
|
214
|
+
|
|
215
|
+
USER_INPUT = "user_input" # Initial: user prompt received
|
|
216
|
+
ASSISTANT = "assistant" # Reasoning/deciding phase
|
|
217
|
+
TOOL_EXECUTION = "tool_execution" # Tool execution phase
|
|
218
|
+
RESPONSE = "response" # Handling results, may complete or loop
|
|
219
|
+
|
|
220
|
+
|
|
212
221
|
@dataclass
|
|
213
222
|
class PlanDoc:
|
|
214
223
|
"""Structured plan document with all required sections."""
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
tunacode/__init__.py,sha256=yUul8igNYMfUrHnYfioIGAqvrH8b5BKiO_pt1wVnmd0,119
|
|
2
|
-
tunacode/constants.py,sha256=
|
|
2
|
+
tunacode/constants.py,sha256=V6bVzLomBc0eSTlVWneBKUo1SYzfKh1pQONyhHANPu0,6100
|
|
3
3
|
tunacode/context.py,sha256=YtfRjUiqsSkk2k9Nn_pjb_m-AXyh6XcOBOJWtFI0wVw,2405
|
|
4
4
|
tunacode/exceptions.py,sha256=m80njR-LqBXhFAEOPqCE7N2QPU4Fkjlf_f6CWKO0_Is,8479
|
|
5
5
|
tunacode/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
tunacode/setup.py,sha256=F1E4zHVnbByu_Uo6AhCJ-W-lIGF_gV6kB84HLAGLmVY,2103
|
|
7
|
-
tunacode/types.py,sha256=
|
|
7
|
+
tunacode/types.py,sha256=xNpDRjIRYg4qGNbl3EG8B13CWAWBoob9ekVm8_6dvnc,10496
|
|
8
8
|
tunacode/cli/__init__.py,sha256=zgs0UbAck8hfvhYsWhWOfBe5oK09ug2De1r4RuQZREA,55
|
|
9
9
|
tunacode/cli/main.py,sha256=DEXS8ODPcsaT1MFBGALX5X7eoohMZDSg6sSW3kOgJxk,3136
|
|
10
|
-
tunacode/cli/repl.py,sha256=
|
|
10
|
+
tunacode/cli/repl.py,sha256=DHI0X8yI9tYvp1PUSR31nTIsp6RF8Aq-WDVHULYPHVA,22516
|
|
11
11
|
tunacode/cli/commands/__init__.py,sha256=J7MZofTaSgspAKP64OavPukj4l53qvkv_-sCfYEUi10,1794
|
|
12
12
|
tunacode/cli/commands/base.py,sha256=Ge_lNQA-GDfcb1Ap1oznCH3UrifBiHH3bA9DNL-tCDw,2519
|
|
13
13
|
tunacode/cli/commands/registry.py,sha256=oAppbaOb-2blVo7Akthu6dbR9UFvXom6wf8m52qftpU,14962
|
|
@@ -43,17 +43,18 @@ tunacode/core/code_index.py,sha256=2qxEn2eTIegV4F_gLeZO5lAOv8mkf4Y_t21whZ9F2Fk,1
|
|
|
43
43
|
tunacode/core/state.py,sha256=aksE0mM2xG-1CkLmpi8cu1n1SOMfqpMuqMBciEVShf0,7893
|
|
44
44
|
tunacode/core/tool_handler.py,sha256=42yUfnq5jgk-0LK93JoJgtsXfVDTf-7hNXyKEfH2FM0,3626
|
|
45
45
|
tunacode/core/agents/__init__.py,sha256=UUJiPYb91arwziSpjd7vIk7XNGA_4HQbsOIbskSqevA,149
|
|
46
|
-
tunacode/core/agents/main.py,sha256=
|
|
46
|
+
tunacode/core/agents/main.py,sha256=50V1FMfJ6lac6XqWqH5KLY_12RDiY-FFgieXbf-tQKM,18217
|
|
47
47
|
tunacode/core/agents/utils.py,sha256=ja6Dwq3AVX6QTddmG2uY5ENxFxr5uzc4TS9OjommXp0,14535
|
|
48
48
|
tunacode/core/agents/agent_components/__init__.py,sha256=CL4XH47T6v_iYy7xCPYjyiEFNOFnkcKwbTuKw6IjKTs,1474
|
|
49
|
-
tunacode/core/agents/agent_components/agent_config.py,sha256=
|
|
50
|
-
tunacode/core/agents/agent_components/agent_helpers.py,sha256
|
|
49
|
+
tunacode/core/agents/agent_components/agent_config.py,sha256=rVoFxmtu8Ly6-UAqTzvyv1NgPYTG5ZuYzJf5Qgo4384,13096
|
|
50
|
+
tunacode/core/agents/agent_components/agent_helpers.py,sha256=FFX-zXDhNoXpzMVe2iznBpNzNtk7OJ2lHf44cfZhTk8,8268
|
|
51
51
|
tunacode/core/agents/agent_components/json_tool_parser.py,sha256=HuyNT0rs-ppx_gLAI2e0XMVGbR_F0WXZfP3sx38VoMg,3447
|
|
52
52
|
tunacode/core/agents/agent_components/message_handler.py,sha256=KJGOtb9VhumgZpxxwO45HrKLhU9_MwuoWRsSQwJviNU,3704
|
|
53
|
-
tunacode/core/agents/agent_components/node_processor.py,sha256=
|
|
54
|
-
tunacode/core/agents/agent_components/response_state.py,sha256=
|
|
53
|
+
tunacode/core/agents/agent_components/node_processor.py,sha256=n_m3FYH_3pQ2AH-OJvoZ1J5mFzAYc_U2TdZph5GAHvQ,22753
|
|
54
|
+
tunacode/core/agents/agent_components/response_state.py,sha256=qnjRSQCYZzac04CcVc4gTvW8epxl4w-Vz0kPjsvM_Qg,4482
|
|
55
55
|
tunacode/core/agents/agent_components/result_wrapper.py,sha256=9CFK0wpsfZx2WT4PBHfkSv22GxL1gAQuUYVMlmYtCJU,1761
|
|
56
|
-
tunacode/core/agents/agent_components/
|
|
56
|
+
tunacode/core/agents/agent_components/state_transition.py,sha256=uyvLJriexosBDQIrxbVDLR_luvXAMG6tnDsX10mbZcI,4077
|
|
57
|
+
tunacode/core/agents/agent_components/task_completion.py,sha256=2mgrnNBTuGHYEPB2MS7ndsG-scqN5Qf1A85mxWZikJE,975
|
|
57
58
|
tunacode/core/agents/agent_components/tool_buffer.py,sha256=09FNtC6zTjiJOL_2CY0b7KDgwdNayGPV6jbENKH6Unc,766
|
|
58
59
|
tunacode/core/agents/agent_components/tool_executor.py,sha256=LlzDwgSLLawwPZQqJ4vfLf-16nhwIiuN8zm8iCeBf1Y,1849
|
|
59
60
|
tunacode/core/agents/agent_components/truncation_checker.py,sha256=XbJ3wwtdC4NhgIMIvFR0z_cfNnYMkiYAZo9zGDBPU8Y,2685
|
|
@@ -76,7 +77,7 @@ tunacode/core/setup/template_setup.py,sha256=0lDGhNVCvGN7ykqHnl3pj4CONH3I2PvMzkm
|
|
|
76
77
|
tunacode/core/token_usage/api_response_parser.py,sha256=plLltHg4zGVzxjv3MFj45bbd-NOJeT_v3P0Ki4zlvn4,1831
|
|
77
78
|
tunacode/core/token_usage/cost_calculator.py,sha256=RjO-O0JENBuGOrWP7QgBZlZxeXC-PAIr8tj_9p_BxOU,2058
|
|
78
79
|
tunacode/core/token_usage/usage_tracker.py,sha256=YUCnF-712nLrbtEvFrsC-VZuYjKUCz3hf-_do6GKSDA,6016
|
|
79
|
-
tunacode/prompts/system.md,sha256=
|
|
80
|
+
tunacode/prompts/system.md,sha256=UxuEwL9LDZKvnIbV7s_q3JQ852Vsvt9pdqqdoPOZRyM,11226
|
|
80
81
|
tunacode/prompts/system.md.bak,sha256=q0gbk_-pvQlNtZBonRo4gNILkKStqNxgDN0ZEwzC3E4,17541
|
|
81
82
|
tunacode/services/__init__.py,sha256=w_E8QK6RnvKSvU866eDe8BCRV26rAm4d3R-Yg06OWCU,19
|
|
82
83
|
tunacode/services/mcp.py,sha256=quO13skECUGt-4QE2NkWk6_8qhmZ5qjgibvw8tUOt-4,3761
|
|
@@ -150,8 +151,8 @@ tunacode/utils/system.py,sha256=J8KqJ4ZqQrNSnM5rrJxPeMk9z2xQQp6dWtI1SKBY1-0,1112
|
|
|
150
151
|
tunacode/utils/text_utils.py,sha256=HAwlT4QMy41hr53cDbbNeNo05MI461TpI9b_xdIv8EY,7288
|
|
151
152
|
tunacode/utils/token_counter.py,sha256=dmFuqVz4ywGFdLfAi5Mg9bAGf8v87Ek-mHU-R3fsYjI,2711
|
|
152
153
|
tunacode/utils/user_configuration.py,sha256=OA-L0BgWNbf9sWpc8lyivgLscwJdpdI8TAYbe0wRs1s,4836
|
|
153
|
-
tunacode_cli-0.0.
|
|
154
|
-
tunacode_cli-0.0.
|
|
155
|
-
tunacode_cli-0.0.
|
|
156
|
-
tunacode_cli-0.0.
|
|
157
|
-
tunacode_cli-0.0.
|
|
154
|
+
tunacode_cli-0.0.75.dist-info/METADATA,sha256=A9xG7bSy8cowstguwUJorSZi6gMEXdrHUc2v9fLQk-o,6898
|
|
155
|
+
tunacode_cli-0.0.75.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
156
|
+
tunacode_cli-0.0.75.dist-info/entry_points.txt,sha256=hbkytikj4dGu6rizPuAd_DGUPBGF191RTnhr9wdhORY,51
|
|
157
|
+
tunacode_cli-0.0.75.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
|
|
158
|
+
tunacode_cli-0.0.75.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|