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.
- coding_agent/__init__.py +20 -0
- coding_agent/_lazy.py +17 -0
- coding_agent/addons/__init__.py +35 -0
- coding_agent/addons/approvals.py +224 -0
- coding_agent/addons/compaction/__init__.py +36 -0
- coding_agent/addons/compaction/addon.py +88 -0
- coding_agent/addons/compaction/compactor.py +138 -0
- coding_agent/addons/compaction/prompts.py +20 -0
- coding_agent/addons/compaction/transcript.py +118 -0
- coding_agent/addons/evaluation/__init__.py +68 -0
- coding_agent/addons/evaluation/addon.py +199 -0
- coding_agent/addons/evaluation/evaluators.py +245 -0
- coding_agent/addons/evaluation/handlers.py +66 -0
- coding_agent/addons/evaluation/policy.py +277 -0
- coding_agent/addons/evaluation/protocol.py +192 -0
- coding_agent/addons/evaluation/state.py +284 -0
- coding_agent/addons/langfuse/__init__.py +24 -0
- coding_agent/addons/langfuse/addon.py +465 -0
- coding_agent/addons/langfuse/install.py +79 -0
- coding_agent/addons/langfuse/serialize.py +158 -0
- coding_agent/addons/learning/__init__.py +33 -0
- coding_agent/addons/learning/addon.py +181 -0
- coding_agent/addons/learning/loop.py +247 -0
- coding_agent/addons/learning/prompts.py +26 -0
- coding_agent/addons/learning/sanitize.py +64 -0
- coding_agent/addons/learning/store.py +522 -0
- coding_agent/addons/manager/__init__.py +18 -0
- coding_agent/addons/manager/bridge.py +74 -0
- coding_agent/addons/manager/discovery.py +34 -0
- coding_agent/addons/manager/report.py +33 -0
- coding_agent/addons/manager/runtime.py +76 -0
- coding_agent/addons/manager/tool_changes.py +16 -0
- coding_agent/addons/persistence/__init__.py +41 -0
- coding_agent/addons/persistence/active.py +132 -0
- coding_agent/addons/persistence/artifacts.py +110 -0
- coding_agent/addons/persistence/collection.py +166 -0
- coding_agent/addons/persistence/compatibility.py +30 -0
- coding_agent/addons/persistence/images.py +133 -0
- coding_agent/addons/persistence/jsonl.py +1195 -0
- coding_agent/addons/persistence/presentation.py +104 -0
- coding_agent/addons/plugins/__init__.py +19 -0
- coding_agent/addons/plugins/install.py +37 -0
- coding_agent/addons/plugins/manager.py +140 -0
- coding_agent/addons/plugins/models.py +50 -0
- coding_agent/addons/skills/__init__.py +28 -0
- coding_agent/addons/skills/addon.py +26 -0
- coding_agent/addons/skills/args.py +78 -0
- coding_agent/addons/skills/bundled_skills/addon-authoring/SKILL.md +77 -0
- coding_agent/addons/skills/bundled_skills/context-budget/SKILL.md +12 -0
- coding_agent/addons/skills/bundled_skills/orient-first/SKILL.md +16 -0
- coding_agent/addons/skills/bundled_skills/security-boundary/SKILL.md +16 -0
- coding_agent/addons/skills/bundled_skills/skill-discipline/SKILL.md +13 -0
- coding_agent/addons/skills/bundled_skills/surgical-edit/SKILL.md +11 -0
- coding_agent/addons/skills/bundled_skills/systematic-debug/SKILL.md +18 -0
- coding_agent/addons/skills/bundled_skills/verify-before-done/SKILL.md +11 -0
- coding_agent/addons/skills/frontmatter.py +57 -0
- coding_agent/addons/skills/models.py +57 -0
- coding_agent/addons/skills/registry.py +109 -0
- coding_agent/addons/skills/tool.py +54 -0
- coding_agent/addons/voice/__init__.py +5 -0
- coding_agent/addons/voice/addon.py +106 -0
- coding_agent/addons/voice/credentials.py +42 -0
- coding_agent/addons/voice/playback.py +42 -0
- coding_agent/addons/voice/speak.py +287 -0
- coding_agent/addons/voice/stream.py +651 -0
- coding_agent/addons/voice/summary.py +126 -0
- coding_agent/agent.py +523 -0
- coding_agent/bench/__init__.py +1 -0
- coding_agent/bench/cli.py +200 -0
- coding_agent/bench/patch.py +78 -0
- coding_agent/commands/__init__.py +5 -0
- coding_agent/commands/catalog.py +179 -0
- coding_agent/commands/jev_argument.py +40 -0
- coding_agent/config/__init__.py +51 -0
- coding_agent/config/config.py +376 -0
- coding_agent/config/credentials.py +153 -0
- coding_agent/config/personalities.json +34 -0
- coding_agent/config/personalities.py +170 -0
- coding_agent/config/prompts.py +67 -0
- coding_agent/protocols/__init__.py +5 -0
- coding_agent/protocols/acp/README.md +36 -0
- coding_agent/protocols/acp/__init__.py +27 -0
- coding_agent/protocols/acp/mapping.py +164 -0
- coding_agent/protocols/acp/server.py +261 -0
- coding_agent/protocols/acp/transport.py +72 -0
- coding_agent/protocols/stdio/README.md +36 -0
- coding_agent/protocols/stdio/__init__.py +37 -0
- coding_agent/protocols/stdio/catalog.py +49 -0
- coding_agent/protocols/stdio/commands.py +218 -0
- coding_agent/protocols/stdio/server.py +181 -0
- coding_agent/protocols/stdio/transport.py +71 -0
- coding_agent/py.typed +0 -0
- coding_agent/resources/__init__.py +5 -0
- coding_agent/resources/usage.py +264 -0
- coding_agent/run/__init__.py +1 -0
- coding_agent/run/cli.py +99 -0
- coding_agent/run/detach.py +37 -0
- coding_agent/run/interrupted.py +67 -0
- coding_agent/run/log_sink.py +46 -0
- coding_agent/tools/__init__.py +73 -0
- coding_agent/tools/ask_user.py +61 -0
- coding_agent/tools/base.py +136 -0
- coding_agent/tools/bash.py +288 -0
- coding_agent/tools/bash_jobs.py +116 -0
- coding_agent/tools/generate_image.py +123 -0
- coding_agent/tools/memory.py +100 -0
- coding_agent/tools/patch.py +218 -0
- coding_agent/tools/read_file.py +167 -0
- coding_agent/tools/search.py +433 -0
- coding_agent/tools/web_search.py +151 -0
- coding_agent/tools/write_file.py +40 -0
- coding_agent/tui/EFFICIENCY_PLAN.md +25 -0
- coding_agent/tui/__init__.py +22 -0
- coding_agent/tui/__main__.py +122 -0
- coding_agent/tui/app.py +456 -0
- coding_agent/tui/chrome/__init__.py +33 -0
- coding_agent/tui/chrome/footer.py +274 -0
- coding_agent/tui/chrome/topbar.py +259 -0
- coding_agent/tui/commands/__init__.py +45 -0
- coding_agent/tui/commands/catalog.py +35 -0
- coding_agent/tui/commands/manager.py +557 -0
- coding_agent/tui/composer/__init__.py +18 -0
- coding_agent/tui/composer/input.py +219 -0
- coding_agent/tui/composer/slash_menu.py +341 -0
- coding_agent/tui/composer/surface.py +330 -0
- coding_agent/tui/composer/voice.py +267 -0
- coding_agent/tui/composer/voice_mode.py +491 -0
- coding_agent/tui/runtime/__init__.py +23 -0
- coding_agent/tui/runtime/events.py +728 -0
- coding_agent/tui/runtime/sink.py +115 -0
- coding_agent/tui/runtime/state.py +145 -0
- coding_agent/tui/runtime/subagent.py +531 -0
- coding_agent/tui/runtime/turn.py +206 -0
- coding_agent/tui/screens/__init__.py +54 -0
- coding_agent/tui/screens/ask.py +69 -0
- coding_agent/tui/screens/context.py +195 -0
- coding_agent/tui/screens/dashboard.py +91 -0
- coding_agent/tui/screens/diff.py +302 -0
- coding_agent/tui/screens/extensions.py +68 -0
- coding_agent/tui/screens/file_selector.py +167 -0
- coding_agent/tui/screens/history.py +458 -0
- coding_agent/tui/screens/langfuse.py +142 -0
- coding_agent/tui/screens/learning.py +113 -0
- coding_agent/tui/screens/modal.py +131 -0
- coding_agent/tui/screens/onboard.py +455 -0
- coding_agent/tui/screens/resume.py +163 -0
- coding_agent/tui/theme/__init__.py +49 -0
- coding_agent/tui/theme/load.py +325 -0
- coding_agent/tui/theme/style.py +95 -0
- coding_agent/tui/theme/theme.toml +1687 -0
- coding_agent/tui/tools/__init__.py +53 -0
- coding_agent/tui/tools/activity.py +184 -0
- coding_agent/tui/tools/calls.py +560 -0
- coding_agent/tui/tools/code_view.py +52 -0
- coding_agent/tui/tools/images.py +250 -0
- coding_agent/tui/tools/labels.py +135 -0
- coding_agent/tui/tools/modal.py +137 -0
- coding_agent/tui/tools/partial_args.py +19 -0
- coding_agent/tui/tools/snapshots.py +382 -0
- coding_agent/tui/transcript/__init__.py +36 -0
- coding_agent/tui/transcript/messages.py +408 -0
- coding_agent/tui/transcript/process.py +504 -0
- coding_agent/tui/transcript/surface.py +345 -0
- coding_agent/tui/transcript/thinking_markdown.py +314 -0
- symphony_code-0.0.1.dist-info/METADATA +66 -0
- symphony_code-0.0.1.dist-info/RECORD +169 -0
- symphony_code-0.0.1.dist-info/WHEEL +4 -0
- symphony_code-0.0.1.dist-info/entry_points.txt +5 -0
- 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
|
+
)
|