yycode 0.3.2__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.
Files changed (131) hide show
  1. agent/__init__.py +33 -0
  2. agent/acp/__init__.py +2 -0
  3. agent/acp/approval_adapter.py +134 -0
  4. agent/acp/content_adapter.py +45 -0
  5. agent/acp/jsonrpc.py +92 -0
  6. agent/acp/server.py +197 -0
  7. agent/acp/session_manager.py +193 -0
  8. agent/acp/update_adapter.py +192 -0
  9. agent/app_paths.py +25 -0
  10. agent/approval.py +169 -0
  11. agent/cancellation.py +52 -0
  12. agent/change_snapshot.py +186 -0
  13. agent/context_compressor.py +116 -0
  14. agent/graph.py +137 -0
  15. agent/llm_retry.py +434 -0
  16. agent/logger.py +97 -0
  17. agent/lsp/__init__.py +13 -0
  18. agent/lsp/client.py +151 -0
  19. agent/lsp/manager.py +234 -0
  20. agent/lsp/types.py +119 -0
  21. agent/message_context_manager.py +322 -0
  22. agent/message_format.py +105 -0
  23. agent/nodes/llm_node.py +58 -0
  24. agent/nodes/state.py +12 -0
  25. agent/nodes/task_guard_node.py +50 -0
  26. agent/nodes/tools_node.py +70 -0
  27. agent/plan_snapshot.py +70 -0
  28. agent/providers/__init__.py +13 -0
  29. agent/providers/anthropic_provider.py +268 -0
  30. agent/providers/base.py +52 -0
  31. agent/providers/openai_provider.py +279 -0
  32. agent/providers/text_tool_calls.py +118 -0
  33. agent/runtime/approval_service.py +184 -0
  34. agent/runtime/context.py +43 -0
  35. agent/runtime/tool_events.py +368 -0
  36. agent/runtime/tool_executor.py +208 -0
  37. agent/runtime/tool_output.py +261 -0
  38. agent/runtime/tool_registry.py +91 -0
  39. agent/runtime/tool_scheduler.py +35 -0
  40. agent/runtime/workflow_guard.py +217 -0
  41. agent/runtime/workspace.py +5 -0
  42. agent/runtime/workspace_tools.py +22 -0
  43. agent/session.py +787 -0
  44. agent/session_replay.py +95 -0
  45. agent/session_store.py +186 -0
  46. agent/skills.py +254 -0
  47. agent/streaming.py +248 -0
  48. agent/subagent.py +634 -0
  49. agent/task_memory.py +340 -0
  50. agent/todo_manager.py +304 -0
  51. agent/tool_retry.py +106 -0
  52. agent/tui/__init__.py +14 -0
  53. agent/tui/app.py +1325 -0
  54. agent/tui/approval.py +53 -0
  55. agent/tui/commands/__init__.py +6 -0
  56. agent/tui/commands/base.py +48 -0
  57. agent/tui/commands/clear.py +37 -0
  58. agent/tui/commands/help.py +27 -0
  59. agent/tui/commands/registry.py +94 -0
  60. agent/tui/help_content.py +108 -0
  61. agent/tui/renderers.py +1961 -0
  62. agent/tui/runner.py +439 -0
  63. agent/tui/state.py +653 -0
  64. main.py +465 -0
  65. tools/__init__.py +50 -0
  66. tools/apply_patch.py +305 -0
  67. tools/bash.py +76 -0
  68. tools/diff_utils.py +139 -0
  69. tools/edit_file.py +40 -0
  70. tools/git_diff.py +72 -0
  71. tools/git_show.py +65 -0
  72. tools/grep.py +149 -0
  73. tools/list_files.py +90 -0
  74. tools/list_skills.py +24 -0
  75. tools/load_skill.py +30 -0
  76. tools/lsp_definition.py +27 -0
  77. tools/lsp_diagnostics.py +32 -0
  78. tools/lsp_document_symbols.py +23 -0
  79. tools/lsp_hover.py +29 -0
  80. tools/lsp_references.py +37 -0
  81. tools/lsp_utils.py +38 -0
  82. tools/lsp_workspace_symbols.py +23 -0
  83. tools/read_file.py +61 -0
  84. tools/read_many_files.py +50 -0
  85. tools/safety.py +50 -0
  86. tools/subagent.py +57 -0
  87. tools/todo.py +89 -0
  88. tools/verify.py +107 -0
  89. tools/web_search.py +250 -0
  90. tools/workspace.py +36 -0
  91. tools/workspace_state.py +60 -0
  92. tools/write_file.py +88 -0
  93. utils/__init__.py +5 -0
  94. utils/retry.py +13 -0
  95. yycode-0.3.2.data/data/skills/code_review.md +61 -0
  96. yycode-0.3.2.data/data/skills/code_workflow.md +404 -0
  97. yycode-0.3.2.data/data/skills/drawio/SKILL.md +636 -0
  98. yycode-0.3.2.data/data/skills/drawio/agents/openai.yaml +19 -0
  99. yycode-0.3.2.data/data/skills/drawio/assets/demo-erd.drawio +84 -0
  100. yycode-0.3.2.data/data/skills/drawio/assets/demo-layered-cn.drawio +91 -0
  101. yycode-0.3.2.data/data/skills/drawio/assets/demo-layered-cn.png +0 -0
  102. yycode-0.3.2.data/data/skills/drawio/assets/demo-layered.drawio +112 -0
  103. yycode-0.3.2.data/data/skills/drawio/assets/demo-layered.png +0 -0
  104. yycode-0.3.2.data/data/skills/drawio/assets/demo-ml.drawio +90 -0
  105. yycode-0.3.2.data/data/skills/drawio/assets/demo-ring-cn.drawio +68 -0
  106. yycode-0.3.2.data/data/skills/drawio/assets/demo-ring-cn.png +0 -0
  107. yycode-0.3.2.data/data/skills/drawio/assets/demo-ring.drawio +86 -0
  108. yycode-0.3.2.data/data/skills/drawio/assets/demo-ring.png +0 -0
  109. yycode-0.3.2.data/data/skills/drawio/assets/demo-sequence.drawio +116 -0
  110. yycode-0.3.2.data/data/skills/drawio/assets/demo-star-cn.drawio +66 -0
  111. yycode-0.3.2.data/data/skills/drawio/assets/demo-star-cn.png +0 -0
  112. yycode-0.3.2.data/data/skills/drawio/assets/demo-star.drawio +79 -0
  113. yycode-0.3.2.data/data/skills/drawio/assets/demo-star.png +0 -0
  114. yycode-0.3.2.data/data/skills/drawio/assets/demo-uml-class.drawio +64 -0
  115. yycode-0.3.2.data/data/skills/drawio/assets/microservices-example.drawio +173 -0
  116. yycode-0.3.2.data/data/skills/drawio/assets/microservices-example.png +0 -0
  117. yycode-0.3.2.data/data/skills/drawio/assets/workflow-cn.drawio +120 -0
  118. yycode-0.3.2.data/data/skills/drawio/assets/workflow-cn.png +0 -0
  119. yycode-0.3.2.data/data/skills/drawio/assets/workflow.drawio +120 -0
  120. yycode-0.3.2.data/data/skills/drawio/assets/workflow.png +0 -0
  121. yycode-0.3.2.data/data/skills/drawio/docs/index.html +469 -0
  122. yycode-0.3.2.data/data/skills/drawio/docs/zh.html +456 -0
  123. yycode-0.3.2.data/data/skills/drawio/references/style-extraction.md +254 -0
  124. yycode-0.3.2.data/data/skills/drawio/styles/schema.json +112 -0
  125. yycode-0.3.2.data/data/skills/plan.md +115 -0
  126. yycode-0.3.2.data/data/skills/ppt/SKILL.md +254 -0
  127. yycode-0.3.2.dist-info/METADATA +12 -0
  128. yycode-0.3.2.dist-info/RECORD +131 -0
  129. yycode-0.3.2.dist-info/WHEEL +5 -0
  130. yycode-0.3.2.dist-info/entry_points.txt +2 -0
  131. yycode-0.3.2.dist-info/top_level.txt +4 -0
agent/streaming.py ADDED
@@ -0,0 +1,248 @@
1
+ """Structured streaming events and renderers."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Awaitable, Callable, Optional
5
+
6
+
7
+ StreamEventCallback = Callable[["StreamEvent"], Awaitable[None]]
8
+ ProviderStreamCallback = Callable[[str, str], Awaitable[None]]
9
+
10
+ ANSI_RESET = "\033[0m"
11
+ ANSI_DIM = "\033[90m"
12
+ ANSI_GREEN = "\033[32m"
13
+ ANSI_RED = "\033[31m"
14
+ ANSI_YELLOW = "\033[33m"
15
+ ANSI_CYAN = "\033[36m"
16
+ ANSI_BG_GREEN = "\033[48;5;22m"
17
+ ANSI_BG_RED = "\033[48;5;52m"
18
+ ANSI_BG_BLUE = "\033[48;5;24m"
19
+ ANSI_BG_GRAY = "\033[48;5;236m"
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class StreamEvent:
24
+ """Structured event emitted by agents during streaming."""
25
+
26
+ source: str
27
+ session_id: str
28
+ event_type: str
29
+ content: str = ""
30
+ role: Optional[str] = None
31
+ parent_session_id: Optional[str] = None
32
+ usage: Optional[dict[str, int]] = None
33
+ title: Optional[str] = None
34
+ detail: Optional[str] = None
35
+ phase: Optional[str] = None
36
+ status: Optional[str] = None
37
+ tool_name: Optional[str] = None
38
+ file_paths: Optional[list[str]] = None
39
+ elapsed_ms: Optional[int] = None
40
+ metadata: Optional[dict[str, Any]] = None
41
+
42
+ def to_dict(self) -> dict:
43
+ """Return a JSON-serializable representation."""
44
+ return {
45
+ "source": self.source,
46
+ "session_id": self.session_id,
47
+ "role": self.role,
48
+ "parent_session_id": self.parent_session_id,
49
+ "event_type": self.event_type,
50
+ "content": self.content,
51
+ "usage": self.usage,
52
+ "title": self.title,
53
+ "detail": self.detail,
54
+ "phase": self.phase,
55
+ "status": self.status,
56
+ "tool_name": self.tool_name,
57
+ "file_paths": self.file_paths,
58
+ "elapsed_ms": self.elapsed_ms,
59
+ "metadata": self.metadata,
60
+ }
61
+
62
+
63
+ def make_provider_stream_callback(
64
+ event_callback: Optional[StreamEventCallback],
65
+ *,
66
+ source: str,
67
+ session_id: str,
68
+ role: Optional[str] = None,
69
+ parent_session_id: Optional[str] = None,
70
+ ) -> Optional[ProviderStreamCallback]:
71
+ """Wrap provider callbacks into structured stream events."""
72
+ if event_callback is None:
73
+ return None
74
+
75
+ async def callback(event_type: str, content: str) -> None:
76
+ await event_callback(
77
+ StreamEvent(
78
+ source=source,
79
+ session_id=session_id,
80
+ role=role,
81
+ parent_session_id=parent_session_id,
82
+ event_type=event_type,
83
+ content=content,
84
+ )
85
+ )
86
+
87
+ return callback
88
+
89
+
90
+ def colorize_diff(diff: str) -> str:
91
+ """Return a diff with background colors for changed line groups."""
92
+ lines = []
93
+ for line in diff.splitlines():
94
+ if line.startswith("@@"):
95
+ lines.append(f"{ANSI_BG_BLUE}{line}{ANSI_RESET}")
96
+ elif line.startswith("diff --git") or line.startswith("index "):
97
+ lines.append(f"{ANSI_BG_GRAY}{line}{ANSI_RESET}")
98
+ elif line.startswith("+++") or line.startswith("---"):
99
+ lines.append(f"{ANSI_BG_GRAY}{line}{ANSI_RESET}")
100
+ elif line.startswith("+"):
101
+ lines.append(f"{ANSI_BG_GREEN}{line}{ANSI_RESET}")
102
+ elif line.startswith("-"):
103
+ lines.append(f"{ANSI_BG_RED}{line}{ANSI_RESET}")
104
+ else:
105
+ lines.append(line)
106
+ if diff.endswith("\n"):
107
+ return "\n".join(lines) + "\n"
108
+ return "\n".join(lines)
109
+
110
+
111
+ class ConsoleStreamRenderer:
112
+ """Render structured stream events to the console."""
113
+
114
+ def __init__(self):
115
+ self.in_thinking_by_session: dict[str, bool] = {}
116
+ self.first_line = True
117
+ self.active_label_by_session: dict[str, str] = {}
118
+
119
+ async def callback(self, event: StreamEvent) -> None:
120
+ """Render a structured stream event."""
121
+ if event.event_type == "thinking_start":
122
+ if not self.first_line:
123
+ print()
124
+ print(f"\033[90m{self._label(event)}Thinking...\033[0m", end="", flush=True)
125
+ self.in_thinking_by_session[event.session_id] = True
126
+ self.first_line = False
127
+ elif event.event_type == "thinking_delta":
128
+ pass
129
+ elif event.event_type == "thinking_end":
130
+ if self.in_thinking_by_session.get(event.session_id):
131
+ print("\033[90m [done]\033[0m", flush=True)
132
+ self.in_thinking_by_session[event.session_id] = False
133
+ elif event.event_type == "tool_start":
134
+ if self.in_thinking_by_session.get(event.session_id):
135
+ print("\033[90m [done]\033[0m")
136
+ self.in_thinking_by_session[event.session_id] = False
137
+ self._print_tool_start(event)
138
+ elif event.event_type == "tool_end":
139
+ self._print_tool_end(event)
140
+ elif event.event_type == "tool_result":
141
+ self._print_tool_result(event)
142
+ elif event.event_type == "text_delta":
143
+ if self.in_thinking_by_session.get(event.session_id):
144
+ print("\033[90m [done]\033[0m")
145
+ self.in_thinking_by_session[event.session_id] = False
146
+ self._print_text_delta(event)
147
+ elif event.event_type == "usage":
148
+ self._print_usage(event)
149
+ elif event.event_type == "context_compressed":
150
+ self._print_context_compressed(event)
151
+ elif event.event_type == "context_summarized":
152
+ self._print_context_summarized(event)
153
+ elif event.event_type in ["llm_waiting", "llm_timeout", "llm_retry", "llm_error"]:
154
+ self._print_llm_status(event)
155
+
156
+ def _print_tool_start(self, event: StreamEvent) -> None:
157
+ if not self.first_line:
158
+ print()
159
+ description = event.title or event.content
160
+ if event.detail and event.detail != description:
161
+ description = f"{description}: {event.detail}"
162
+ print(f"\033[90m{self._label(event)}▶ Starting {description}...\033[0m", end="", flush=True)
163
+ self.first_line = False
164
+
165
+ def _print_tool_end(self, event: StreamEvent) -> None:
166
+ print("\033[90m [done]\033[0m", flush=True)
167
+
168
+ def _print_tool_result(self, event: StreamEvent) -> None:
169
+ if not event.content.strip():
170
+ return
171
+ if not self.first_line:
172
+ print()
173
+ print(f"\033[90m{self._label(event)}diff:\033[0m")
174
+ print(colorize_diff(event.content), flush=True)
175
+ self.first_line = False
176
+
177
+ def _print_text_delta(self, event: StreamEvent) -> None:
178
+ label = self._label(event)
179
+ active_label = self.active_label_by_session.get(event.session_id)
180
+ if label and active_label != label:
181
+ if not self.first_line:
182
+ print()
183
+ print(label, end="", flush=True)
184
+ self.active_label_by_session[event.session_id] = label
185
+ print(event.content, end="", flush=True)
186
+ self.first_line = False
187
+
188
+ def _label(self, event: StreamEvent) -> str:
189
+ if event.source != "subagent":
190
+ return ""
191
+ role = event.role or "unknown"
192
+ return f"\033[90m@{role} \033[0m"
193
+
194
+ def _print_usage(self, event: StreamEvent) -> None:
195
+ usage = event.usage or {}
196
+ label = self._label(event)
197
+ if not self.first_line:
198
+ print()
199
+ print(
200
+ f"\033[90m{label}[usage] "
201
+ f"input={usage.get('input_tokens', 0)} "
202
+ f"output={usage.get('output_tokens', 0)} "
203
+ f"total={usage.get('total_tokens', 0)}\033[0m",
204
+ flush=True,
205
+ )
206
+ self.first_line = False
207
+
208
+ def _print_context_compressed(self, event: StreamEvent) -> None:
209
+ if not self.first_line:
210
+ print()
211
+ print(f"\033[90m[context] {event.content}\033[0m", flush=True)
212
+ self.first_line = False
213
+
214
+ def _print_context_summarized(self, event: StreamEvent) -> None:
215
+ if not self.first_line:
216
+ print()
217
+ print(f"\033[90m[context] {event.content}\033[0m", flush=True)
218
+ self.first_line = False
219
+
220
+ def _print_llm_status(self, event: StreamEvent) -> None:
221
+ """Print LLM status updates so user knows the agent is still working."""
222
+ if self.in_thinking_by_session.get(event.session_id):
223
+ print("\033[90m [waiting]\033[0m")
224
+ self.in_thinking_by_session[event.session_id] = False
225
+ if not self.first_line:
226
+ print()
227
+
228
+ color = ANSI_DIM
229
+ prefix = "[llm]"
230
+ if event.event_type == "llm_error":
231
+ color = ANSI_RED
232
+ prefix = "[error]"
233
+ elif event.event_type == "llm_timeout":
234
+ color = ANSI_YELLOW
235
+ prefix = "[timeout]"
236
+ elif event.event_type == "llm_retry":
237
+ color = ANSI_CYAN
238
+ prefix = "[retry]"
239
+ elif event.event_type == "llm_waiting":
240
+ color = ANSI_DIM
241
+ prefix = "[waiting]"
242
+
243
+ print(f"{color}{self._label(event)}{prefix} {event.content}{ANSI_RESET}", flush=True)
244
+ self.first_line = False
245
+
246
+
247
+ class StreamPrinter(ConsoleStreamRenderer):
248
+ """Backward-compatible console stream printer."""