python-agent-harness 1.5.6.5__tar.gz → 1.5.6.6__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.
Files changed (84) hide show
  1. {python_agent_harness-1.5.6.5/python_agent_harness.egg-info → python_agent_harness-1.5.6.6}/PKG-INFO +1 -1
  2. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/pyproject.toml +1 -1
  3. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/__init__.py +1 -1
  4. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/core/context_manager.py +9 -6
  5. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/agent.md +0 -15
  6. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/core.py +18 -4
  7. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/subagent.md +0 -14
  8. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/session/config.py +12 -0
  9. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/session/session.py +8 -6
  10. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tui/render.py +6 -4
  11. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6/python_agent_harness.egg-info}/PKG-INFO +1 -1
  12. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/LICENSE +0 -0
  13. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/README.md +0 -0
  14. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/__main__.py +0 -0
  15. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/core/__init__.py +0 -0
  16. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/core/agent.py +0 -0
  17. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/core/models.py +0 -0
  18. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/core/planmode.py +0 -0
  19. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/core/subagent.py +0 -0
  20. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/core/token_estimator.py +0 -0
  21. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/core/tool_runner.py +0 -0
  22. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/entry/__init__.py +0 -0
  23. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/entry/cli.py +0 -0
  24. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/entry/controller.py +0 -0
  25. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/entry/headless.py +0 -0
  26. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/entry/restore.py +0 -0
  27. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/entry/server.py +0 -0
  28. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/entry/view.py +0 -0
  29. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/io/__init__.py +0 -0
  30. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/io/attachments.py +0 -0
  31. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/io/diffrender.py +0 -0
  32. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/io/persistence.py +0 -0
  33. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/io/text_filter.py +0 -0
  34. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/llm/__init__.py +0 -0
  35. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/llm/client.py +0 -0
  36. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/mcp/__init__.py +0 -0
  37. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/mcp/client.py +0 -0
  38. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/mcp/config.py +0 -0
  39. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/mcp/manager.py +0 -0
  40. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/__init__.py +0 -0
  41. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/agents/reviewer.md +0 -0
  42. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/build-switch.md +0 -0
  43. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/commands/explain.md +0 -0
  44. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/compact.md +0 -0
  45. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/initialize.md +0 -0
  46. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/plan-mode.md +0 -0
  47. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/plan.md +0 -0
  48. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/review.md +0 -0
  49. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/summary.md +0 -0
  50. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/task-completion-rules.md +0 -0
  51. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/prompts/title.md +0 -0
  52. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/session/__init__.py +0 -0
  53. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/session/commands.py +0 -0
  54. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/__init__.py +0 -0
  55. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/agent_tool.py +0 -0
  56. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/base.py +0 -0
  57. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/bash.py +0 -0
  58. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/diffapply.py +0 -0
  59. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/edit.py +0 -0
  60. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/edit_mac.py +0 -0
  61. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/filesystem.py +0 -0
  62. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/glob.py +0 -0
  63. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/glob_mac.py +0 -0
  64. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/grep.py +0 -0
  65. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/grep_mac.py +0 -0
  66. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/mcp.py +0 -0
  67. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/planexit.py +0 -0
  68. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/question.py +0 -0
  69. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/read.py +0 -0
  70. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/skill.py +0 -0
  71. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/todo.py +0 -0
  72. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tools/write.py +0 -0
  73. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tui/__init__.py +0 -0
  74. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tui/commands.py +0 -0
  75. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tui/core.py +0 -0
  76. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness/tui/input.py +0 -0
  77. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness.egg-info/SOURCES.txt +0 -0
  78. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness.egg-info/dependency_links.txt +0 -0
  79. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness.egg-info/entry_points.txt +0 -0
  80. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness.egg-info/requires.txt +0 -0
  81. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/python_agent_harness.egg-info/top_level.txt +0 -0
  82. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/setup.cfg +0 -0
  83. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/tests/test_invariants.py +0 -0
  84. {python_agent_harness-1.5.6.5 → python_agent_harness-1.5.6.6}/tests/test_scenarios.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-agent-harness
3
- Version: 1.5.6.5
3
+ Version: 1.5.6.6
4
4
  Summary: A lightweight, hackable mini-OpenCode written in Python.
5
5
  Author-email: Huming Chen <chenhuming@gmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-agent-harness"
7
- version = "1.5.6.5"
7
+ version = "1.5.6.6"
8
8
  description = "A lightweight, hackable mini-OpenCode written in Python."
9
9
  authors = [
10
10
  {name = "Huming Chen", email = "chenhuming@gmail.com"}
@@ -7,7 +7,7 @@ from .mcp.config import MCPConfig, MCPServerConfig
7
7
  from .mcp.manager import MCPManager
8
8
  from .session.session import Session
9
9
 
10
- __version__ = "1.5.6.5"
10
+ __version__ = "1.5.6.6"
11
11
 
12
12
  __all__ = [
13
13
  "Client",
@@ -65,10 +65,11 @@ class ContextManager:
65
65
  def compact(self) -> bool:
66
66
  """Compact the conversation; return True on success.
67
67
 
68
- On success the history is replaced by the summary frame followed
69
- by every real user prompt (nudges and other harness-injected
70
- messages excluded), so the model keeps the actual requests; the
71
- last prompt is the resume request for the next round.
68
+ On success the history is replaced by the summary frame, every
69
+ real user prompt (nudges and other harness-injected messages
70
+ excluded), and a trailing resume message telling the model what
71
+ is already done — the summary and the prompts alone end on a
72
+ bare user request, which invites redoing finished work.
72
73
  """
73
74
  loop = self.loop
74
75
  prompts = user_prompt_texts(loop.messages)
@@ -87,8 +88,10 @@ class ContextManager:
87
88
  # stays untouched): it is part of the user turn, never a
88
89
  # system message. Every real user prompt (nudges and other
89
90
  # harness-injected messages excluded) is preserved verbatim
90
- # after the frame, so the model keeps the actual requests.
91
- loop.messages = compacted_messages(summary, prompts)
91
+ # after the frame, so the model keeps the actual requests,
92
+ # and the trailing resume message states that the summarised
93
+ # work is done (injected flag: TUI and filters skip it).
94
+ loop.messages = compacted_messages(summary, prompts, resume=True)
92
95
  # The shared conversation now is the compacted one: mirror it
93
96
  # onto session.last_messages so the TUI (renders from it) and
94
97
  # a later manual /compact start from the summary, not the old
@@ -69,21 +69,6 @@ The user will primarily request you perform software engineering tasks. This inc
69
69
  - Batch independent tool calls in a single response to maximize parallelism.
70
70
 
71
71
  # Tool usage instructions
72
- **Specialized Tools vs. Shell Commands (CRITICAL):**
73
- - NEVER use `Bash` for file operations with grep, find, ls, cat, head, tail, sed or awk.
74
- - ALWAYS use: `Glob`, `Grep`, `Read`, `Edit`, `Write`
75
- - Reserve `Bash` EXCLUSIVELY for: git, npm, docker, cargo, make, system services and other non-file commands
76
- - Using bash for file operations violates the tool hierarchy and creates technical debt
77
-
78
- **Tool Selection Hierarchy:**
79
- - File search by name → Use `Glob` (NOT find or ls)
80
- - Directory listing → Use `Glob` with glob pattern `"*"` (not ls)
81
- - Content search → Use `Grep` (NOT grep or rg)
82
- - Read files → Use `Read` (NOT cat/head/tail)
83
- - Edit files → Use `Edit` (NOT sed/awk)
84
- - Write files → Use `Write` (NOT echo >/cat <<EOF)
85
- - System operations → Use `Bash` (for git, npm, docker, etc.)
86
-
87
72
  {{TOOL_INSTRUCTIONS}}
88
73
 
89
74
  # Code References
@@ -500,7 +500,11 @@ def user_prompt_texts(messages: list) -> list[str]:
500
500
  (``remember_user_text`` excludes reminders for title generation —
501
501
  a different purpose, so the filters differ.)
502
502
  """
503
- nudge = config.NUDGE_MESSAGE
503
+ # Harness-injected one-liners replayed verbatim on every compaction
504
+ # would accumulate: the completion nudge, and the resume message the
505
+ # automatic compaction appends (a bare "keep going" carries no task
506
+ # content — the summary already covers the progress it refers to).
507
+ injected = {config.NUDGE_MESSAGE, config.RESUME_MESSAGE}
504
508
  is_reminder: list[bool] = []
505
509
  last_reminder = -1
506
510
  for msg in messages:
@@ -523,7 +527,7 @@ def user_prompt_texts(messages: list) -> list[str]:
523
527
  if _message_role(msg) != "user":
524
528
  continue
525
529
  text = _message_text(msg)
526
- if not text or text == nudge:
530
+ if not text or text in injected:
527
531
  continue
528
532
  if text.startswith(config.COMPACT_HEADER):
529
533
  continue
@@ -587,19 +591,29 @@ def compact_summary(
587
591
  return summary or None
588
592
 
589
593
 
590
- def compacted_messages(summary: str, prompts: list[str]) -> list[Message]:
594
+ def compacted_messages(summary: str, prompts: list[str], resume: bool = False) -> list[Message]:
591
595
  """The post-compaction history: the summary frame as a user message
592
596
  followed by every preserved user prompt, oldest first.
593
597
 
594
598
  The summary lives in the user turn (the system prompt is passed
595
599
  separately and stays untouched); *prompts* are the real user
596
600
  requests (see ``user_prompt_texts``) that must survive compaction.
601
+
602
+ *resume* appends ``config.RESUME_MESSAGE`` (flagged ``injected``, so
603
+ the TUI and the filters skip it) after the prompts: without it the
604
+ rebuilt history ends with a bare user request and the model may redo
605
+ work the summary already records. Only the automatic in-loop
606
+ compaction passes it — the manual /compact command waits for the
607
+ next user message instead.
597
608
  """
598
609
  frame = (config.COMPACT_HEADER + summary + config.COMPACT_SEPARATOR).strip()
599
- return [
610
+ messages = [
600
611
  Message(role="user", content=frame),
601
612
  *[Message(role="user", content=p) for p in prompts],
602
613
  ]
614
+ if resume:
615
+ messages.append(Message(role="user", content=config.RESUME_MESSAGE, injected=True))
616
+ return messages
603
617
 
604
618
 
605
619
  # ---- custom agent discovery -----------------------------------------------
@@ -20,26 +20,12 @@ You are an autonomous subagent. Your role is to independently complete well-defi
20
20
  You MUST answer concisely with fewer than 4 lines of text (not including tool use or code generation), unless user asks for detail.
21
21
 
22
22
  # Tool usage instructions
23
- **Specialized Tools vs. Shell Commands (CRITICAL):**
24
- - NEVER use `Bash` for file operations with grep, find, ls, cat, head, tail, sed or awk.
25
- - ALWAYS use: `Glob`, `Grep`, `Read`, `Edit`, `Write`
26
- - Reserve `Bash` EXCLUSIVELY for: git, npm, docker, cargo, make, system services and other non-file commands
27
- - Using bash for file operations violates the tool hierarchy and creates technical debt
28
23
 
29
24
  **Parallel Tool Execution:**
30
25
  - Call multiple tools in a single response when tasks are independent
31
26
  - Never use placeholders or guess missing parameters
32
27
  - Maximize parallel execution to improve efficiency
33
28
 
34
- **Tool Selection Hierarchy:**
35
- - File search by name → Use `Glob` (NOT find or ls)
36
- - Directory listing → Use `Glob` with glob pattern `"*"` (not ls)
37
- - Content search → Use `Grep` (NOT grep or rg)
38
- - Read files → Use `Read` (NOT cat/head/tail)
39
- - Edit files → Use `Edit` (NOT sed/awk)
40
- - Write files → Use `Write` (NOT echo >/cat <<EOF)
41
- - System operations → Use `Bash` (for git, npm, docker, etc.)
42
-
43
29
  {{TOOL_INSTRUCTIONS}}
44
30
 
45
31
  # Output Requirements
@@ -44,6 +44,18 @@ NUDGE_MESSAGE = (
44
44
  # ---- compaction -----------------------------------------------------------
45
45
  COMPACT_HEADER = "**[Compacted Summary]**\n\n"
46
46
  COMPACT_SEPARATOR = "\n\n---\n\n**[Context compacted]**\n\n---\n\n"
47
+ # Appended after the summary frame + preserved prompts on the AUTOMATIC
48
+ # path only (AgentLoop -> ContextManager.compact). The rebuilt history
49
+ # ends with the original user request, and a bare request reads as "start
50
+ # here", so the model tends to redo work the summary already records. The
51
+ # manual /compact command does NOT inject it: it replaces the history and
52
+ # waits for the next user message.
53
+ RESUME_MESSAGE = (
54
+ "Compaction just finished; the summary above records the work already done. "
55
+ "Do not repeat completed steps or re-run finished tool calls. "
56
+ "Review which parts of the request above are already done "
57
+ "and continue with the unfinished ones."
58
+ )
47
59
 
48
60
  # ---- token calibration ----------------------------------------------------
49
61
  CALIBRATION_MIN = 0.5
@@ -670,14 +670,16 @@ class Session:
670
670
  def remember_user_text(self, messages: list) -> None:
671
671
  """Remember the last real user message for session-title generation.
672
672
 
673
- Skips harness-injected messages (nudges, plan/build-switch
674
- reminders, queued mode prompts — flagged ``injected``) so a
675
- title is never generated from "Review the original user request
676
- and the Task Completion Rules…" or a mode-switch reminder.
673
+ Skips harness-injected messages (nudges, the post-compaction
674
+ resume message, plan/build-switch reminders, queued mode prompts
675
+ — flagged ``injected``; the content check also covers restored
676
+ sessions, where the flag is lost) so a title is never generated
677
+ from "Review the original user request and the Task Completion
678
+ Rules…" or a mode-switch reminder.
677
679
  """
678
- nudge = config.NUDGE_MESSAGE
680
+ injected = {config.NUDGE_MESSAGE, config.RESUME_MESSAGE}
679
681
  for m in reversed(messages):
680
- if m.role == "user" and not m.injected and m.text() != nudge:
682
+ if m.role == "user" and not m.injected and m.text() not in injected:
681
683
  self.store.remember_first_user_message(m.text())
682
684
  break
683
685
 
@@ -144,11 +144,13 @@ def _is_injected_user_text(text: str) -> bool:
144
144
 
145
145
  Live sessions flag injected messages directly; these content checks
146
146
  cover restored sessions, where the flag is lost in the markdown
147
- round-trip: the completion nudge, the <system-reminder>-wrapped
148
- plan/build-switch prompts (plan.md, plan-mode.md, build-switch.md
149
- all start with the reminder tag), and the plan-exit approval notice.
147
+ round-trip: the completion nudge, the post-compaction resume message,
148
+ the <system-reminder>-wrapped plan/build-switch prompts (plan.md,
149
+ plan-mode.md, build-switch.md all start with the reminder tag), and
150
+ the plan-exit approval notice.
150
151
  """
151
- if text == config.NUDGE_MESSAGE:
152
+ injected = {config.NUDGE_MESSAGE, config.RESUME_MESSAGE}
153
+ if text in injected:
152
154
  return True
153
155
  return _is_mode_reminder_text(text)
154
156
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-agent-harness
3
- Version: 1.5.6.5
3
+ Version: 1.5.6.6
4
4
  Summary: A lightweight, hackable mini-OpenCode written in Python.
5
5
  Author-email: Huming Chen <chenhuming@gmail.com>
6
6
  License-Expression: MIT