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,133 @@
|
|
|
1
|
+
"""Session-local content-addressed images; remote URLs are never fetched."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import base64
|
|
5
|
+
import binascii
|
|
6
|
+
import hashlib
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from core_ai.content import IMAGE_MIME_BY_SUFFIX, normalize_content
|
|
11
|
+
from coding_agent.addons.persistence.artifacts import publish, safe_path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
_IMAGE_TYPES = {"image", "image_url", "input_image"}
|
|
15
|
+
# Inline image parts only. A local path is read when the user attached it.
|
|
16
|
+
MAX_IMAGE_BYTES = 8 * 1024 * 1024
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def archive(value: Any, directory: Path) -> Any:
|
|
20
|
+
"""Copy embedded image parts to disk without mutating caller data."""
|
|
21
|
+
if isinstance(value, list):
|
|
22
|
+
return [archive(item, directory) for item in value]
|
|
23
|
+
if not isinstance(value, dict):
|
|
24
|
+
return value
|
|
25
|
+
if value.get("type") in _IMAGE_TYPES or any(
|
|
26
|
+
key in value for key in ("inlineData", "inline_data", "fileData", "file_data")
|
|
27
|
+
):
|
|
28
|
+
if value.get("archive_path"):
|
|
29
|
+
try:
|
|
30
|
+
_image_path(value["archive_path"], directory)
|
|
31
|
+
except ValueError:
|
|
32
|
+
return _unavailable(value, "invalid")
|
|
33
|
+
return dict(value)
|
|
34
|
+
parts = normalize_content([value])
|
|
35
|
+
if parts and parts[0].get("type") == "image":
|
|
36
|
+
part = parts[0]
|
|
37
|
+
payload = _payload(value, part)
|
|
38
|
+
if payload is not None:
|
|
39
|
+
digest = hashlib.sha256(payload).hexdigest()
|
|
40
|
+
relative = f"images/{digest}"
|
|
41
|
+
path = _image_path(relative, directory)
|
|
42
|
+
if not path.exists():
|
|
43
|
+
publish(path, payload)
|
|
44
|
+
return {
|
|
45
|
+
"type": "image",
|
|
46
|
+
"media_type": part.get("media_type", "image/png"),
|
|
47
|
+
**({"filename": part["filename"]} if part.get("filename") else {}),
|
|
48
|
+
"archive_path": relative,
|
|
49
|
+
}
|
|
50
|
+
return {key: archive(item, directory) for key, item in value.items()}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def hydrate(value: Any, directory: Path) -> Any:
|
|
54
|
+
"""Restore image bytes. A bad or missing blob does not fail the caller."""
|
|
55
|
+
if isinstance(value, list):
|
|
56
|
+
return [hydrate(item, directory) for item in value]
|
|
57
|
+
if not isinstance(value, dict):
|
|
58
|
+
return value
|
|
59
|
+
reference = value.get("archive_path")
|
|
60
|
+
if reference is not None:
|
|
61
|
+
try:
|
|
62
|
+
path = _image_path(reference, directory)
|
|
63
|
+
size = path.stat().st_size
|
|
64
|
+
if size > MAX_IMAGE_BYTES:
|
|
65
|
+
return _unavailable(value, "too_large")
|
|
66
|
+
payload = path.read_bytes()
|
|
67
|
+
except (OSError, ValueError):
|
|
68
|
+
return _unavailable(value, "missing")
|
|
69
|
+
digest = hashlib.sha256(payload).hexdigest()
|
|
70
|
+
if f"images/{digest}" != str(reference):
|
|
71
|
+
return _unavailable(value, "mismatch")
|
|
72
|
+
return {
|
|
73
|
+
"type": "image",
|
|
74
|
+
"media_type": value.get("media_type", "image/png"),
|
|
75
|
+
**({"filename": value["filename"]} if value.get("filename") else {}),
|
|
76
|
+
"data": base64.b64encode(payload).decode("ascii"),
|
|
77
|
+
"url": path.as_uri(),
|
|
78
|
+
}
|
|
79
|
+
return {key: hydrate(item, directory) for key, item in value.items()}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _payload(value: dict[str, Any], part: dict[str, Any]) -> bytes | None:
|
|
83
|
+
data = part.get("data")
|
|
84
|
+
if data:
|
|
85
|
+
try:
|
|
86
|
+
payload = base64.b64decode("".join(str(data).split()), validate=True)
|
|
87
|
+
except (ValueError, binascii.Error):
|
|
88
|
+
return None
|
|
89
|
+
if len(payload) > MAX_IMAGE_BYTES:
|
|
90
|
+
return None
|
|
91
|
+
return payload
|
|
92
|
+
return _attached_file(value)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _attached_file(value: dict[str, Any]) -> bytes | None:
|
|
96
|
+
"""Read only a path the user attached. Arbitrary file URLs stay untouched."""
|
|
97
|
+
if value.get("user_attached") is not True:
|
|
98
|
+
return None
|
|
99
|
+
raw = value.get("attached_path")
|
|
100
|
+
if not isinstance(raw, str) or not raw:
|
|
101
|
+
return None
|
|
102
|
+
path = Path(raw).expanduser()
|
|
103
|
+
try:
|
|
104
|
+
if not path.is_absolute() or path.is_symlink() or not path.is_file():
|
|
105
|
+
return None
|
|
106
|
+
if path.suffix.lower() not in IMAGE_MIME_BY_SUFFIX:
|
|
107
|
+
return None
|
|
108
|
+
if path.stat().st_size > MAX_IMAGE_BYTES:
|
|
109
|
+
return None
|
|
110
|
+
return path.read_bytes()
|
|
111
|
+
except OSError:
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _unavailable(value: dict[str, Any], reason: str) -> dict[str, Any]:
|
|
116
|
+
kept = {
|
|
117
|
+
key: value[key]
|
|
118
|
+
for key in ("type", "media_type", "filename", "archive_path")
|
|
119
|
+
if key in value
|
|
120
|
+
}
|
|
121
|
+
kept.setdefault("type", "image")
|
|
122
|
+
kept["unavailable"] = reason
|
|
123
|
+
return kept
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _image_path(reference: Any, directory: Path) -> Path:
|
|
127
|
+
reference = str(reference)
|
|
128
|
+
digest = reference.removeprefix("images/")
|
|
129
|
+
if reference != f"images/{digest}" or len(digest) != 64 or any(
|
|
130
|
+
char not in "0123456789abcdef" for char in digest
|
|
131
|
+
):
|
|
132
|
+
raise ValueError(f"invalid image archive reference: {reference!r}")
|
|
133
|
+
return safe_path(directory, reference)
|