symphony-code 0.0.1__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 (169) hide show
  1. coding_agent/__init__.py +20 -0
  2. coding_agent/_lazy.py +17 -0
  3. coding_agent/addons/__init__.py +35 -0
  4. coding_agent/addons/approvals.py +224 -0
  5. coding_agent/addons/compaction/__init__.py +36 -0
  6. coding_agent/addons/compaction/addon.py +88 -0
  7. coding_agent/addons/compaction/compactor.py +138 -0
  8. coding_agent/addons/compaction/prompts.py +20 -0
  9. coding_agent/addons/compaction/transcript.py +118 -0
  10. coding_agent/addons/evaluation/__init__.py +68 -0
  11. coding_agent/addons/evaluation/addon.py +199 -0
  12. coding_agent/addons/evaluation/evaluators.py +245 -0
  13. coding_agent/addons/evaluation/handlers.py +66 -0
  14. coding_agent/addons/evaluation/policy.py +277 -0
  15. coding_agent/addons/evaluation/protocol.py +192 -0
  16. coding_agent/addons/evaluation/state.py +284 -0
  17. coding_agent/addons/langfuse/__init__.py +24 -0
  18. coding_agent/addons/langfuse/addon.py +465 -0
  19. coding_agent/addons/langfuse/install.py +79 -0
  20. coding_agent/addons/langfuse/serialize.py +158 -0
  21. coding_agent/addons/learning/__init__.py +33 -0
  22. coding_agent/addons/learning/addon.py +181 -0
  23. coding_agent/addons/learning/loop.py +247 -0
  24. coding_agent/addons/learning/prompts.py +26 -0
  25. coding_agent/addons/learning/sanitize.py +64 -0
  26. coding_agent/addons/learning/store.py +522 -0
  27. coding_agent/addons/manager/__init__.py +18 -0
  28. coding_agent/addons/manager/bridge.py +74 -0
  29. coding_agent/addons/manager/discovery.py +34 -0
  30. coding_agent/addons/manager/report.py +33 -0
  31. coding_agent/addons/manager/runtime.py +76 -0
  32. coding_agent/addons/manager/tool_changes.py +16 -0
  33. coding_agent/addons/persistence/__init__.py +41 -0
  34. coding_agent/addons/persistence/active.py +132 -0
  35. coding_agent/addons/persistence/artifacts.py +110 -0
  36. coding_agent/addons/persistence/collection.py +166 -0
  37. coding_agent/addons/persistence/compatibility.py +30 -0
  38. coding_agent/addons/persistence/images.py +133 -0
  39. coding_agent/addons/persistence/jsonl.py +1195 -0
  40. coding_agent/addons/persistence/presentation.py +104 -0
  41. coding_agent/addons/plugins/__init__.py +19 -0
  42. coding_agent/addons/plugins/install.py +37 -0
  43. coding_agent/addons/plugins/manager.py +140 -0
  44. coding_agent/addons/plugins/models.py +50 -0
  45. coding_agent/addons/skills/__init__.py +28 -0
  46. coding_agent/addons/skills/addon.py +26 -0
  47. coding_agent/addons/skills/args.py +78 -0
  48. coding_agent/addons/skills/bundled_skills/addon-authoring/SKILL.md +77 -0
  49. coding_agent/addons/skills/bundled_skills/context-budget/SKILL.md +12 -0
  50. coding_agent/addons/skills/bundled_skills/orient-first/SKILL.md +16 -0
  51. coding_agent/addons/skills/bundled_skills/security-boundary/SKILL.md +16 -0
  52. coding_agent/addons/skills/bundled_skills/skill-discipline/SKILL.md +13 -0
  53. coding_agent/addons/skills/bundled_skills/surgical-edit/SKILL.md +11 -0
  54. coding_agent/addons/skills/bundled_skills/systematic-debug/SKILL.md +18 -0
  55. coding_agent/addons/skills/bundled_skills/verify-before-done/SKILL.md +11 -0
  56. coding_agent/addons/skills/frontmatter.py +57 -0
  57. coding_agent/addons/skills/models.py +57 -0
  58. coding_agent/addons/skills/registry.py +109 -0
  59. coding_agent/addons/skills/tool.py +54 -0
  60. coding_agent/addons/voice/__init__.py +5 -0
  61. coding_agent/addons/voice/addon.py +106 -0
  62. coding_agent/addons/voice/credentials.py +42 -0
  63. coding_agent/addons/voice/playback.py +42 -0
  64. coding_agent/addons/voice/speak.py +287 -0
  65. coding_agent/addons/voice/stream.py +651 -0
  66. coding_agent/addons/voice/summary.py +126 -0
  67. coding_agent/agent.py +523 -0
  68. coding_agent/bench/__init__.py +1 -0
  69. coding_agent/bench/cli.py +200 -0
  70. coding_agent/bench/patch.py +78 -0
  71. coding_agent/commands/__init__.py +5 -0
  72. coding_agent/commands/catalog.py +179 -0
  73. coding_agent/commands/jev_argument.py +40 -0
  74. coding_agent/config/__init__.py +51 -0
  75. coding_agent/config/config.py +376 -0
  76. coding_agent/config/credentials.py +153 -0
  77. coding_agent/config/personalities.json +34 -0
  78. coding_agent/config/personalities.py +170 -0
  79. coding_agent/config/prompts.py +67 -0
  80. coding_agent/protocols/__init__.py +5 -0
  81. coding_agent/protocols/acp/README.md +36 -0
  82. coding_agent/protocols/acp/__init__.py +27 -0
  83. coding_agent/protocols/acp/mapping.py +164 -0
  84. coding_agent/protocols/acp/server.py +261 -0
  85. coding_agent/protocols/acp/transport.py +72 -0
  86. coding_agent/protocols/stdio/README.md +36 -0
  87. coding_agent/protocols/stdio/__init__.py +37 -0
  88. coding_agent/protocols/stdio/catalog.py +49 -0
  89. coding_agent/protocols/stdio/commands.py +218 -0
  90. coding_agent/protocols/stdio/server.py +181 -0
  91. coding_agent/protocols/stdio/transport.py +71 -0
  92. coding_agent/py.typed +0 -0
  93. coding_agent/resources/__init__.py +5 -0
  94. coding_agent/resources/usage.py +264 -0
  95. coding_agent/run/__init__.py +1 -0
  96. coding_agent/run/cli.py +99 -0
  97. coding_agent/run/detach.py +37 -0
  98. coding_agent/run/interrupted.py +67 -0
  99. coding_agent/run/log_sink.py +46 -0
  100. coding_agent/tools/__init__.py +73 -0
  101. coding_agent/tools/ask_user.py +61 -0
  102. coding_agent/tools/base.py +136 -0
  103. coding_agent/tools/bash.py +288 -0
  104. coding_agent/tools/bash_jobs.py +116 -0
  105. coding_agent/tools/generate_image.py +123 -0
  106. coding_agent/tools/memory.py +100 -0
  107. coding_agent/tools/patch.py +218 -0
  108. coding_agent/tools/read_file.py +167 -0
  109. coding_agent/tools/search.py +433 -0
  110. coding_agent/tools/web_search.py +151 -0
  111. coding_agent/tools/write_file.py +40 -0
  112. coding_agent/tui/EFFICIENCY_PLAN.md +25 -0
  113. coding_agent/tui/__init__.py +22 -0
  114. coding_agent/tui/__main__.py +122 -0
  115. coding_agent/tui/app.py +456 -0
  116. coding_agent/tui/chrome/__init__.py +33 -0
  117. coding_agent/tui/chrome/footer.py +274 -0
  118. coding_agent/tui/chrome/topbar.py +259 -0
  119. coding_agent/tui/commands/__init__.py +45 -0
  120. coding_agent/tui/commands/catalog.py +35 -0
  121. coding_agent/tui/commands/manager.py +557 -0
  122. coding_agent/tui/composer/__init__.py +18 -0
  123. coding_agent/tui/composer/input.py +219 -0
  124. coding_agent/tui/composer/slash_menu.py +341 -0
  125. coding_agent/tui/composer/surface.py +330 -0
  126. coding_agent/tui/composer/voice.py +267 -0
  127. coding_agent/tui/composer/voice_mode.py +491 -0
  128. coding_agent/tui/runtime/__init__.py +23 -0
  129. coding_agent/tui/runtime/events.py +728 -0
  130. coding_agent/tui/runtime/sink.py +115 -0
  131. coding_agent/tui/runtime/state.py +145 -0
  132. coding_agent/tui/runtime/subagent.py +531 -0
  133. coding_agent/tui/runtime/turn.py +206 -0
  134. coding_agent/tui/screens/__init__.py +54 -0
  135. coding_agent/tui/screens/ask.py +69 -0
  136. coding_agent/tui/screens/context.py +195 -0
  137. coding_agent/tui/screens/dashboard.py +91 -0
  138. coding_agent/tui/screens/diff.py +302 -0
  139. coding_agent/tui/screens/extensions.py +68 -0
  140. coding_agent/tui/screens/file_selector.py +167 -0
  141. coding_agent/tui/screens/history.py +458 -0
  142. coding_agent/tui/screens/langfuse.py +142 -0
  143. coding_agent/tui/screens/learning.py +113 -0
  144. coding_agent/tui/screens/modal.py +131 -0
  145. coding_agent/tui/screens/onboard.py +455 -0
  146. coding_agent/tui/screens/resume.py +163 -0
  147. coding_agent/tui/theme/__init__.py +49 -0
  148. coding_agent/tui/theme/load.py +325 -0
  149. coding_agent/tui/theme/style.py +95 -0
  150. coding_agent/tui/theme/theme.toml +1687 -0
  151. coding_agent/tui/tools/__init__.py +53 -0
  152. coding_agent/tui/tools/activity.py +184 -0
  153. coding_agent/tui/tools/calls.py +560 -0
  154. coding_agent/tui/tools/code_view.py +52 -0
  155. coding_agent/tui/tools/images.py +250 -0
  156. coding_agent/tui/tools/labels.py +135 -0
  157. coding_agent/tui/tools/modal.py +137 -0
  158. coding_agent/tui/tools/partial_args.py +19 -0
  159. coding_agent/tui/tools/snapshots.py +382 -0
  160. coding_agent/tui/transcript/__init__.py +36 -0
  161. coding_agent/tui/transcript/messages.py +408 -0
  162. coding_agent/tui/transcript/process.py +504 -0
  163. coding_agent/tui/transcript/surface.py +345 -0
  164. coding_agent/tui/transcript/thinking_markdown.py +314 -0
  165. symphony_code-0.0.1.dist-info/METADATA +66 -0
  166. symphony_code-0.0.1.dist-info/RECORD +169 -0
  167. symphony_code-0.0.1.dist-info/WHEEL +4 -0
  168. symphony_code-0.0.1.dist-info/entry_points.txt +5 -0
  169. symphony_code-0.0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,53 @@
1
+ """Tool-call widgets and image attachments for the TUI."""
2
+
3
+ from coding_agent.tui.tools.calls import (
4
+ BashToolHeader,
5
+ BashToolWidget,
6
+ GenerateImageWidget,
7
+ PatchDiffWidget,
8
+ ReadFileWidget,
9
+ ToolCallWidget,
10
+ diff_stats,
11
+ make_tool_widget,
12
+ make_unified_diff,
13
+ )
14
+ from coding_agent.tui.tools.modal import ToolCallModal
15
+ from coding_agent.tui.tools.images import (
16
+ IMAGE_MARKER_RE,
17
+ ImageAttachment,
18
+ ImageModal,
19
+ build_user_content,
20
+ display_from_content,
21
+ dropped_image_paths,
22
+ textual_image,
23
+ )
24
+ from coding_agent.tui.tools.snapshots import (
25
+ CompletedRunSummary,
26
+ ToolCallSnapshot,
27
+ ToolCallSummary,
28
+ snapshot_from_call,
29
+ )
30
+
31
+ __all__ = [
32
+ "BashToolHeader",
33
+ "BashToolWidget",
34
+ "CompletedRunSummary",
35
+ "GenerateImageWidget",
36
+ "IMAGE_MARKER_RE",
37
+ "ImageAttachment",
38
+ "ImageModal",
39
+ "PatchDiffWidget",
40
+ "ReadFileWidget",
41
+ "ToolCallModal",
42
+ "ToolCallSnapshot",
43
+ "ToolCallSummary",
44
+ "ToolCallWidget",
45
+ "build_user_content",
46
+ "diff_stats",
47
+ "display_from_content",
48
+ "dropped_image_paths",
49
+ "make_tool_widget",
50
+ "make_unified_diff",
51
+ "snapshot_from_call",
52
+ "textual_image",
53
+ ]
@@ -0,0 +1,184 @@
1
+ """Activity metadata for live tool cards and Explored folds.
2
+
3
+ Explored partitioning keys on ``activity.group``. Without a group, different
4
+ reasons still fold together via ``bool(reason)``: the reason is a label, not a
5
+ partition key.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import random
12
+ from dataclasses import dataclass
13
+ from typing import Any, Mapping, Sequence
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class Activity:
18
+ verb: str = ""
19
+ reason: str = ""
20
+ group: str = ""
21
+
22
+
23
+ _ACTIVITY_KEYS = ("activity", "verb", "reason", "goal", "group")
24
+
25
+
26
+ def parse_activity(activity: Mapping[str, Any] | str | None) -> Activity:
27
+ """Normalize ``verb`` / ``reason`` / ``group`` from a tool activity mapping."""
28
+ if isinstance(activity, str):
29
+ return Activity(reason=_activity_text(activity))
30
+ if not isinstance(activity, Mapping):
31
+ return Activity()
32
+ return Activity(
33
+ verb=_activity_text(activity.get("verb")),
34
+ reason=_activity_text(activity.get("reason") or activity.get("goal")),
35
+ group=_activity_text(activity.get("group")),
36
+ )
37
+
38
+
39
+ def take_activity(arguments: dict[str, Any]) -> Activity:
40
+ """Pop UI activity from nested or flattened tool arguments and normalize it."""
41
+ nested = arguments.pop("activity", None)
42
+ parsed = parse_activity(nested)
43
+ verb = parsed.verb or _activity_text(arguments.pop("verb", None))
44
+ reason = parsed.reason or _activity_text(
45
+ arguments.pop("reason", None) or arguments.pop("goal", None)
46
+ )
47
+ group = parsed.group or _activity_text(arguments.pop("group", None))
48
+ return Activity(verb=verb, reason=reason, group=group)
49
+
50
+
51
+ def strip_activity_json(raw: str) -> str:
52
+ """Drop nested and flattened activity fields from a raw JSON argument blob."""
53
+ if not raw:
54
+ return raw
55
+ try:
56
+ parsed = json.loads(raw)
57
+ except json.JSONDecodeError:
58
+ return raw
59
+ if not isinstance(parsed, dict):
60
+ return raw
61
+ for key in _ACTIVITY_KEYS:
62
+ parsed.pop(key, None)
63
+ return json.dumps(parsed, separators=(",", ":"))
64
+
65
+
66
+ def same_activity_group(left: Any, right: Any) -> bool:
67
+ """Whether two calls belong in the same Explored row.
68
+
69
+ Group wins when either side has one. Without a group, labeled and unlabeled
70
+ calls stay apart, but distinct reasons still fold together.
71
+ """
72
+ left_activity = _activity_of(left)
73
+ right_activity = _activity_of(right)
74
+ if left_activity.group or right_activity.group:
75
+ return left_activity.group == right_activity.group
76
+ return bool(left_activity.reason) == bool(right_activity.reason)
77
+
78
+
79
+ def explored_activity(calls: Sequence[Any]) -> Activity:
80
+ """First non-empty verb/reason/group already on an Explored row."""
81
+ verb = reason = group = ""
82
+ for call in calls:
83
+ activity = _activity_of(call)
84
+ verb = verb or activity.verb
85
+ reason = reason or activity.reason
86
+ group = group or activity.group
87
+ if verb and reason and group:
88
+ break
89
+ return Activity(verb=verb, reason=reason, group=group)
90
+
91
+
92
+ def format_explored_duration(calls: Sequence[Any]) -> str:
93
+ """Sum snapshot durations when the row has an activity verb; else empty."""
94
+ if not any(_activity_of(call).verb for call in calls):
95
+ return ""
96
+ durations = [
97
+ duration
98
+ for call in calls
99
+ if (duration := getattr(call, "duration", None)) is not None
100
+ ]
101
+ if not durations:
102
+ return ""
103
+ seconds = sum(durations)
104
+ if seconds < 1:
105
+ return "<1s"
106
+ if seconds < 60:
107
+ return f"{seconds:.1f}s"
108
+ return f"{seconds / 60:.1f}m"
109
+
110
+
111
+ def explored_count_label(*, tool_count: int, thought_count: int) -> str:
112
+ parts: list[str] = []
113
+ if tool_count:
114
+ noun = "tool" if tool_count == 1 else "tools"
115
+ parts.append(f"{tool_count} {noun}")
116
+ if thought_count:
117
+ parts.append(f"{thought_count} thought{'s' if thought_count != 1 else ''}")
118
+ return " · ".join(parts) or "0 tools"
119
+
120
+
121
+ def explored_title(calls: Sequence[Any], *, thought_count: int = 0) -> str:
122
+ activity = explored_activity(calls)
123
+ title = activity.verb or activity.reason or "Explored"
124
+ failed = sum(getattr(call, "status", "") == "failed" for call in calls)
125
+ suffix = f" · {failed} failed" if failed else ""
126
+ duration = format_explored_duration(calls)
127
+ timing = f" for {duration}" if duration else ""
128
+ return (
129
+ f"{_clip_title(title)} · "
130
+ f"{explored_count_label(tool_count=len(calls), thought_count=thought_count)}"
131
+ f"{timing}{suffix}"
132
+ )
133
+
134
+
135
+ def _clip_title(title: str) -> str:
136
+ text = title.strip()
137
+ if len(text) <= 96:
138
+ return text
139
+ return f"{text[:96].rstrip()}…"
140
+
141
+
142
+ # Playful past-tense completion verbs in the spirit of Claude Code's spinner
143
+ # words. Shown as-is on the folded run collection: "Stargazed for 2m 2s".
144
+ COMPLETION_VERBS = (
145
+ "Stargazed",
146
+ "Cooked",
147
+ "Pondered",
148
+ "Noodled",
149
+ "Wandered",
150
+ "Tinkered",
151
+ "Mused",
152
+ "Orbited",
153
+ "Whisked",
154
+ "Grooved",
155
+ "Spelunked",
156
+ "Percolated",
157
+ "Ruminated",
158
+ "Concocted",
159
+ "Meandered",
160
+ "Incubated",
161
+ "Harmonized",
162
+ "Forged",
163
+ "Sketched",
164
+ "Vibed",
165
+ )
166
+
167
+
168
+ def choose_completion_verb() -> str:
169
+ """Pick a Claude-style verb for the completed-run collection."""
170
+ return random.choice(COMPLETION_VERBS)
171
+
172
+
173
+ def _activity_text(value: Any) -> str:
174
+ return value.strip() if isinstance(value, str) else ""
175
+
176
+
177
+ def _activity_of(item: Any) -> Activity:
178
+ if isinstance(item, Activity):
179
+ return item
180
+ return Activity(
181
+ verb=getattr(item, "activity_verb", "") or "",
182
+ reason=getattr(item, "activity_reason", "") or "",
183
+ group=getattr(item, "activity_group", "") or "",
184
+ )