tamfis-code 0.6.3__tar.gz → 0.6.5__tar.gz
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.
- {tamfis_code-0.6.3/tamfis_code.egg-info → tamfis_code-0.6.5}/PKG-INFO +1 -1
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/pyproject.toml +1 -1
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/__init__.py +1 -1
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/live_input.py +7 -2
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/runner_local.py +50 -2
- {tamfis_code-0.6.3 → tamfis_code-0.6.5/tamfis_code.egg-info}/PKG-INFO +1 -1
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_live_input.py +14 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_runner_local.py +23 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_workspace_scope.py +11 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/MANIFEST.in +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/README.md +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/USAGE_INSTALL_RELEASE.md +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/setup.cfg +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/__main__.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/agent_definitions.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/agents.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/api_client.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/cli.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/clipboard.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/completion.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/config.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/custom_commands.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/doctor.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/enforcer.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/evidence.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/hooks.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/indexer.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/instructions.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/interactive.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/local_chat.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/local_tools.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/mcp.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/mcp_stdio_server.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/metrics.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/model_registry.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/__init__.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/agent.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/agent_server.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/automation.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/compat.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/conversation.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/delegation.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/events.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/leases.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/security.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/skills.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/tools.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/openhands/workspace.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/orchestrator/__init__.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/orchestrator/context.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/orchestrator/engine.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/orchestrator/planner.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/orchestrator/protocols.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/orchestrator/validator.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/project.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/provider_protocols.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/providers.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/pty.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/references.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/render.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/routing.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/runner.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/safety.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/screenshot.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/sessions.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/state.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/swarm.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/tasks.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/tool_policy.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code/workspace.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code.egg-info/SOURCES.txt +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code.egg-info/dependency_links.txt +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code.egg-info/entry_points.txt +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code.egg-info/requires.txt +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tamfis_code.egg-info/top_level.txt +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_agent_definitions.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_agents.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_agents_delegation.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_build_installation.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_cli_commands.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_clipboard.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_completion.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_context_rollover.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_custom_commands.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_cwd_validation.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_doctor_session_diagnostics.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_enforcer.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_fake_tool_detection.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_find_references.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_hooks.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_indexer.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_instructions.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_integration_new.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_interactive_standalone.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_local_pty.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_mcp.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_mcp_search_bounds.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_mcp_standalone_degradation.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_mcp_stdio_server.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_metrics.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_openhands_runtime.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_openrouter_cost_routing.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_orchestrator.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_plan_mode_gate.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_plan_steps_and_attempts.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_provider_protocols.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_reasoning_effort_capability.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_reasoning_plan.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_references.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_render_task_diagnostics.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_routing.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_runner_helpers.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_safety.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_safety_manifest.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_sessions.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_swarm.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_tamfis_code_api_client.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_tamfis_code_approval.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_tamfis_code_approval_flow.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_tamfis_code_config.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_tamfis_code_intent.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_tamfis_code_render.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_tamfis_code_repl_exit.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_tamfis_code_tasks.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_tamfis_code_workspace.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_version_consistency.py +0 -0
- {tamfis_code-0.6.3 → tamfis_code-0.6.5}/tests/test_workspace_discovery_gaps.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tamfis-code
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
4
4
|
Summary: TamfisGPT Code -- portable subscription-backed terminal coding agent with local tools, PTY, plans, queues, and approvals
|
|
5
5
|
Project-URL: Homepage, https://github.com/tamfitronics/tamfis-code
|
|
6
6
|
Project-URL: Repository, https://github.com/tamfitronics/tamfis-code
|
|
@@ -17,7 +17,7 @@ See docs/REMOTE_AGENT_MASTER_SPEC.md, Phase 21, for the original --remote
|
|
|
17
17
|
architecture's spec.
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
-
__version__ = "0.6.
|
|
20
|
+
__version__ = "0.6.5"
|
|
21
21
|
|
|
22
22
|
# Bumped whenever a CLI release requires a minimum backend Remote API
|
|
23
23
|
# contract version. Only meaningful for --remote; the standalone path has no
|
|
@@ -125,12 +125,17 @@ class LiveInputListener:
|
|
|
125
125
|
|
|
126
126
|
def _dispatch(self) -> None:
|
|
127
127
|
buf = bytes(self._buf)
|
|
128
|
-
|
|
128
|
+
# os.read() is allowed to return more than one byte. A keypress can
|
|
129
|
+
# therefore arrive together with terminal noise or a paste prefix;
|
|
130
|
+
# equality-only matching silently discarded Ctrl+T in that case and
|
|
131
|
+
# made the in-task editor appear broken. Consume the control byte
|
|
132
|
+
# wherever it occurs, while keeping the editor single-flight.
|
|
133
|
+
if _CTRL_T in buf:
|
|
129
134
|
self._buf.clear()
|
|
130
135
|
if self._interject_task is None or self._interject_task.done():
|
|
131
136
|
self._interject_task = asyncio.ensure_future(self._interject())
|
|
132
137
|
return
|
|
133
|
-
if
|
|
138
|
+
if _SHIFT_TAB in buf:
|
|
134
139
|
self._buf.clear()
|
|
135
140
|
self._cycle_mode()
|
|
136
141
|
return
|
|
@@ -275,7 +275,11 @@ def _objective_excluded_names(workspace_root: str, objective: str) -> set[str]:
|
|
|
275
275
|
def _detect_workspace_scope(workspace_root: str, objective: str) -> list[Path]:
|
|
276
276
|
"""Resolve the smallest useful set of project roots for this turn.
|
|
277
277
|
|
|
278
|
-
Exact directory names mentioned by the user win.
|
|
278
|
+
Exact directory names mentioned by the user win. Explicit absolute paths
|
|
279
|
+
in the objective are also authoritative task-local scope expansions. This
|
|
280
|
+
matters when the CLI is launched from a parent/admin checkout but the
|
|
281
|
+
user names a sibling project such as `/home/tamfisseo/package.json`.
|
|
282
|
+
A request containing
|
|
279
283
|
"stack" under /home/tamfisgpt selects the canonical TamfisGPT stack roots.
|
|
280
284
|
Otherwise, when the workspace itself is a project, it remains the scope;
|
|
281
285
|
when it is merely a parent directory, only immediate child projects are
|
|
@@ -289,6 +293,32 @@ def _detect_workspace_scope(workspace_root: str, objective: str) -> list[Path]:
|
|
|
289
293
|
root = Path(workspace_root).expanduser().resolve()
|
|
290
294
|
lowered = (objective or "").lower()
|
|
291
295
|
|
|
296
|
+
# CWD is the default boundary, but an explicitly named existing absolute
|
|
297
|
+
# project path is an intentional request to inspect that project. Resolve
|
|
298
|
+
# package/manifests to their containing project directory and keep the
|
|
299
|
+
# expansion bounded to the project marker -- never treat an arbitrary
|
|
300
|
+
# absolute path or filesystem parent as a workspace.
|
|
301
|
+
explicit_roots: list[Path] = []
|
|
302
|
+
for raw_path in re.findall(r"(?<![\w.-])/(?:[A-Za-z0-9._~+\-]+/)*[A-Za-z0-9._~+\-]+", objective or ""):
|
|
303
|
+
candidate = Path(raw_path.rstrip(".,;:)]}"))
|
|
304
|
+
try:
|
|
305
|
+
candidate = candidate.resolve()
|
|
306
|
+
if candidate.is_file():
|
|
307
|
+
candidate = candidate.parent
|
|
308
|
+
if candidate.is_dir() and _is_project_root(candidate):
|
|
309
|
+
explicit_roots.append(candidate)
|
|
310
|
+
except OSError:
|
|
311
|
+
continue
|
|
312
|
+
if explicit_roots:
|
|
313
|
+
deduped: list[Path] = []
|
|
314
|
+
seen_explicit: set[str] = set()
|
|
315
|
+
for candidate in explicit_roots:
|
|
316
|
+
key = str(candidate)
|
|
317
|
+
if key not in seen_explicit:
|
|
318
|
+
seen_explicit.add(key)
|
|
319
|
+
deduped.append(candidate)
|
|
320
|
+
return deduped
|
|
321
|
+
|
|
292
322
|
try:
|
|
293
323
|
children = [child for child in root.iterdir() if child.is_dir()]
|
|
294
324
|
except OSError:
|
|
@@ -1153,6 +1183,14 @@ def _legacy_resume_messages(state, incoming_objective: str) -> tuple[list[dict[s
|
|
|
1153
1183
|
# the tail of the accumulated text, and triggers within a couple KB of a
|
|
1154
1184
|
# genuine loop, long before the content can grow large enough to matter.
|
|
1155
1185
|
_DEGENERATE_REPETITION_RE = re.compile(r"(.{6,200}?)\1{4,}", re.DOTALL)
|
|
1186
|
+
# A separate guard for a common context-confusion failure: the model starts
|
|
1187
|
+
# replaying a synthetic transcript ("then the user said ... then the
|
|
1188
|
+
# assistant responded ...") instead of answering the current task. The
|
|
1189
|
+
# individual paragraphs are often too long or slightly varied for the
|
|
1190
|
+
# contiguous backreference above, so count the unmistakable transcript
|
|
1191
|
+
# markers in the rolling tail.
|
|
1192
|
+
_CONVERSATION_ECHO_USER_MARKER = re.compile(r"\bthen\s+the\s+user\s+said\b", re.IGNORECASE)
|
|
1193
|
+
_CONVERSATION_ECHO_ASSISTANT_MARKER = re.compile(r"\bthen\s+the\s+assistant\s+(?:said|responded)\b", re.IGNORECASE)
|
|
1156
1194
|
_DEGENERATE_REPETITION_TAIL_WINDOW = 8_000
|
|
1157
1195
|
_STREAM_QUALITY_LAG_CHARS = 1_024
|
|
1158
1196
|
_CORRUPTED_STREAM_MIN_CHARS = 600
|
|
@@ -2247,6 +2285,12 @@ async def _stream_one_completion(
|
|
|
2247
2285
|
if _DEGENERATE_REPETITION_RE.search(tail_buffer):
|
|
2248
2286
|
quality_failure_reason = "degenerate_repetition"
|
|
2249
2287
|
break
|
|
2288
|
+
if (
|
|
2289
|
+
len(_CONVERSATION_ECHO_USER_MARKER.findall(tail_buffer)) >= 3
|
|
2290
|
+
and len(_CONVERSATION_ECHO_ASSISTANT_MARKER.findall(tail_buffer)) >= 3
|
|
2291
|
+
):
|
|
2292
|
+
quality_failure_reason = "conversation_echo"
|
|
2293
|
+
break
|
|
2250
2294
|
if _corrupted_lexical_stream_index(tail_buffer) is not None:
|
|
2251
2295
|
quality_failure_reason = "corrupted_output"
|
|
2252
2296
|
break
|
|
@@ -2278,6 +2322,8 @@ async def _stream_one_completion(
|
|
|
2278
2322
|
diagnosis = (
|
|
2279
2323
|
"Detected corrupted provider token output; discarding it and requesting a clean route."
|
|
2280
2324
|
if quality_failure_reason == "corrupted_output"
|
|
2325
|
+
else "Detected a repeated conversation transcript; discarding it and requesting a clean route."
|
|
2326
|
+
if quality_failure_reason == "conversation_echo"
|
|
2281
2327
|
else "Detected the model repeating itself in a loop; discarding that completion."
|
|
2282
2328
|
)
|
|
2283
2329
|
renderer.handle_event({
|
|
@@ -3604,7 +3650,7 @@ async def run_local_agent_turn(
|
|
|
3604
3650
|
renderer.handle_event({"event_type": "ai_task_failed", "payload": {"error": message}})
|
|
3605
3651
|
return TaskOutcome(status="failed", error=message)
|
|
3606
3652
|
|
|
3607
|
-
if finish_reason in {"degenerate_repetition", "corrupted_output"}:
|
|
3653
|
+
if finish_reason in {"degenerate_repetition", "conversation_echo", "corrupted_output"}:
|
|
3608
3654
|
failed_quality_provider = resolved_provider
|
|
3609
3655
|
quality_failed_providers.add(failed_quality_provider)
|
|
3610
3656
|
checkpoint_partial_parts.clear()
|
|
@@ -3647,6 +3693,8 @@ async def run_local_agent_turn(
|
|
|
3647
3693
|
reason_text = (
|
|
3648
3694
|
"corrupted token output"
|
|
3649
3695
|
if finish_reason == "corrupted_output"
|
|
3696
|
+
else "a repeated conversation transcript"
|
|
3697
|
+
if finish_reason == "conversation_echo"
|
|
3650
3698
|
else "a degenerate repetition loop"
|
|
3651
3699
|
)
|
|
3652
3700
|
error = (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tamfis-code
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
4
4
|
Summary: TamfisGPT Code -- portable subscription-backed terminal coding agent with local tools, PTY, plans, queues, and approvals
|
|
5
5
|
Project-URL: Homepage, https://github.com/tamfitronics/tamfis-code
|
|
6
6
|
Project-URL: Repository, https://github.com/tamfitronics/tamfis-code
|
|
@@ -174,6 +174,20 @@ class CtrlTInjectsFollowUpTests(_StatePatchMixin, unittest.IsolatedAsyncioTestCa
|
|
|
174
174
|
import asyncio
|
|
175
175
|
asyncio.run(_run())
|
|
176
176
|
|
|
177
|
+
def test_ctrl_t_is_detected_when_read_with_adjacent_terminal_bytes(self):
|
|
178
|
+
async def _run():
|
|
179
|
+
renderer = StreamRenderer(_console())
|
|
180
|
+
cfg = _config("ask")
|
|
181
|
+
listener = LiveInputListener(session_id=1, renderer=renderer, cli_config=cfg)
|
|
182
|
+
with patch.object(listener, "_interject", new_callable=AsyncMock) as mocked:
|
|
183
|
+
listener._buf = bytearray(b"x" + _CTRL_T + b"y")
|
|
184
|
+
listener._dispatch()
|
|
185
|
+
await asyncio.sleep(0)
|
|
186
|
+
mocked.assert_awaited_once()
|
|
187
|
+
|
|
188
|
+
import asyncio
|
|
189
|
+
asyncio.run(_run())
|
|
190
|
+
|
|
177
191
|
import asyncio
|
|
178
192
|
asyncio.run(_run())
|
|
179
193
|
|
|
@@ -767,6 +767,29 @@ class RunLocalAgentTurnTests(_StatePatchMixin, unittest.TestCase):
|
|
|
767
767
|
)
|
|
768
768
|
self.assertNotIn(looping_phrase, visible)
|
|
769
769
|
|
|
770
|
+
def test_repeated_conversation_transcript_is_stopped_early(self):
|
|
771
|
+
with tempfile.TemporaryDirectory() as ws:
|
|
772
|
+
rounds = [[
|
|
773
|
+
_chunk(_delta(content=(
|
|
774
|
+
'Then the user said "audit the site". Then the assistant responded '
|
|
775
|
+
"with a long analysis. " if index % 2 else
|
|
776
|
+
'Then the user said "continue until fixed". Then the assistant responded '
|
|
777
|
+
"with another long analysis. "
|
|
778
|
+
))) for index in range(20)
|
|
779
|
+
]]
|
|
780
|
+
client = _FakeClient(rounds)
|
|
781
|
+
manager = _FakeManager(client)
|
|
782
|
+
renderer = _RecordingRenderer()
|
|
783
|
+
outcome = asyncio.run(run_local_agent_turn(
|
|
784
|
+
manager, ProviderType.NVIDIA, None,
|
|
785
|
+
[{"role": "user", "content": "summarize the site"}],
|
|
786
|
+
self._console(), renderer,
|
|
787
|
+
workspace_root=ws, session_id=1, approval_policy="auto", interactive=False,
|
|
788
|
+
))
|
|
789
|
+
self.assertEqual(outcome.status, "failed")
|
|
790
|
+
self.assertIn("repeated conversation transcript", outcome.error)
|
|
791
|
+
self.assertEqual(len(client.calls), 1)
|
|
792
|
+
|
|
770
793
|
def test_change_request_completed_with_no_mutation_gets_a_caveat(self):
|
|
771
794
|
"""Confirmed live: a weak model can narrate "I'll fix this" -- complete
|
|
772
795
|
with a fabricated "corrected" code block -- without ever calling
|
|
@@ -79,6 +79,17 @@ class DetectWorkspaceScopeTests(unittest.TestCase):
|
|
|
79
79
|
scope = _detect_workspace_scope(str(root), "fix the bug")
|
|
80
80
|
self.assertEqual(scope, [root.resolve()])
|
|
81
81
|
|
|
82
|
+
def test_explicit_absolute_project_path_expands_scope_from_admin_cwd(self):
|
|
83
|
+
with tempfile.TemporaryDirectory() as parent, tempfile.TemporaryDirectory() as admin:
|
|
84
|
+
project = Path(parent) / "tamfisseo"
|
|
85
|
+
project.mkdir()
|
|
86
|
+
(project / "package.json").write_text("{}")
|
|
87
|
+
scope = _detect_workspace_scope(
|
|
88
|
+
str(Path(admin)),
|
|
89
|
+
f"check the project at {project / 'package.json'} and fix its bugs",
|
|
90
|
+
)
|
|
91
|
+
self.assertEqual(scope, [project.resolve()])
|
|
92
|
+
|
|
82
93
|
def test_explicitly_named_root_is_honored_even_if_archived(self):
|
|
83
94
|
"""A user who explicitly names a backup directory is making a
|
|
84
95
|
deliberate request -- classification only governs *implicit*
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|