agent-riggs 0.1.0__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.
- agent_riggs/__init__.py +3 -0
- agent_riggs/assembly.py +53 -0
- agent_riggs/briefing/__init__.py +0 -0
- agent_riggs/briefing/project.py +21 -0
- agent_riggs/briefing/session.py +84 -0
- agent_riggs/cli.py +255 -0
- agent_riggs/config.py +103 -0
- agent_riggs/defaults/config.toml +35 -0
- agent_riggs/ingest/__init__.py +0 -0
- agent_riggs/ingest/pipeline.py +141 -0
- agent_riggs/ingest/sources/__init__.py +0 -0
- agent_riggs/ingest/sources/base.py +18 -0
- agent_riggs/ingest/sources/blq.py +18 -0
- agent_riggs/ingest/sources/fledgling.py +18 -0
- agent_riggs/ingest/sources/jetsam.py +18 -0
- agent_riggs/ingest/sources/kibitzer.py +80 -0
- agent_riggs/mcp/__init__.py +0 -0
- agent_riggs/mcp/server.py +28 -0
- agent_riggs/metrics/__init__.py +0 -0
- agent_riggs/metrics/compute.py +76 -0
- agent_riggs/metrics/trends.py +44 -0
- agent_riggs/plugins/__init__.py +0 -0
- agent_riggs/plugins/base.py +37 -0
- agent_riggs/plugins/briefing.py +40 -0
- agent_riggs/plugins/ingest.py +47 -0
- agent_riggs/plugins/metrics.py +59 -0
- agent_riggs/plugins/ratchet.py +91 -0
- agent_riggs/plugins/sandbox.py +51 -0
- agent_riggs/plugins/trust.py +163 -0
- agent_riggs/ratchet/__init__.py +0 -0
- agent_riggs/ratchet/aggregator.py +23 -0
- agent_riggs/ratchet/candidates.py +113 -0
- agent_riggs/ratchet/history.py +24 -0
- agent_riggs/ratchet/promotions.py +33 -0
- agent_riggs/sandbox/__init__.py +0 -0
- agent_riggs/sandbox/grades.py +27 -0
- agent_riggs/sandbox/integration.py +15 -0
- agent_riggs/sandbox/recommendations.py +23 -0
- agent_riggs/service.py +35 -0
- agent_riggs/store.py +38 -0
- agent_riggs/trust/__init__.py +0 -0
- agent_riggs/trust/events.py +30 -0
- agent_riggs/trust/ewma.py +65 -0
- agent_riggs/trust/scorer.py +22 -0
- agent_riggs/trust/transitions.py +79 -0
- agent_riggs-0.1.0.dist-info/METADATA +19 -0
- agent_riggs-0.1.0.dist-info/RECORD +49 -0
- agent_riggs-0.1.0.dist-info/WHEEL +4 -0
- agent_riggs-0.1.0.dist-info/entry_points.txt +2 -0
agent_riggs/__init__.py
ADDED
agent_riggs/assembly.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Build a RiggsService from discovered plugins."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from agent_riggs.config import load_config
|
|
8
|
+
from agent_riggs.service import RiggsService
|
|
9
|
+
from agent_riggs.store import Store
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def assemble(project_root: Path, read_only: bool = False) -> RiggsService:
|
|
13
|
+
"""Build the service with all discovered plugins."""
|
|
14
|
+
config = load_config(project_root)
|
|
15
|
+
store_path = project_root / config.store.path
|
|
16
|
+
store = Store(store_path, read_only=read_only)
|
|
17
|
+
service = RiggsService(project_root, store, config)
|
|
18
|
+
|
|
19
|
+
_register_core_plugins(service)
|
|
20
|
+
_register_optional_plugins(service)
|
|
21
|
+
|
|
22
|
+
ddl: list[str] = []
|
|
23
|
+
for p in service.plugins.values():
|
|
24
|
+
ddl.extend(p.schema_ddl())
|
|
25
|
+
if not read_only:
|
|
26
|
+
store.ensure_schema(ddl)
|
|
27
|
+
|
|
28
|
+
return service
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _register_core_plugins(service: RiggsService) -> None:
|
|
32
|
+
"""Register plugins that are always available."""
|
|
33
|
+
from agent_riggs.plugins.briefing import BriefingPlugin
|
|
34
|
+
from agent_riggs.plugins.ingest import IngestPlugin
|
|
35
|
+
from agent_riggs.plugins.metrics import MetricsPlugin
|
|
36
|
+
from agent_riggs.plugins.ratchet import RatchetPlugin
|
|
37
|
+
from agent_riggs.plugins.trust import TrustPlugin
|
|
38
|
+
|
|
39
|
+
service.register(TrustPlugin())
|
|
40
|
+
service.register(IngestPlugin())
|
|
41
|
+
service.register(RatchetPlugin())
|
|
42
|
+
service.register(MetricsPlugin())
|
|
43
|
+
service.register(BriefingPlugin())
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _register_optional_plugins(service: RiggsService) -> None:
|
|
47
|
+
"""Register plugins whose backing tools may not be installed."""
|
|
48
|
+
import shutil
|
|
49
|
+
|
|
50
|
+
if shutil.which("blq"):
|
|
51
|
+
from agent_riggs.plugins.sandbox import SandboxPlugin
|
|
52
|
+
|
|
53
|
+
service.register(SandboxPlugin())
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Project-level health assessment."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from agent_riggs.store import Store
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def project_health(store: Store, project: str) -> dict[str, Any]:
|
|
11
|
+
row = store.execute(
|
|
12
|
+
"""SELECT count(*), coalesce(avg(trust_end), 1.0), coalesce(avg(failure_rate), 0.0)
|
|
13
|
+
FROM session_summaries WHERE project = ?""",
|
|
14
|
+
[project],
|
|
15
|
+
).fetchone()
|
|
16
|
+
return {
|
|
17
|
+
"total_sessions": row[0],
|
|
18
|
+
"avg_trust": row[1],
|
|
19
|
+
"avg_failure_rate": row[2],
|
|
20
|
+
"healthy": row[1] >= 0.7 and row[2] <= 0.3,
|
|
21
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Generate session briefings from cross-session data."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from agent_riggs.config import RiggsConfig
|
|
9
|
+
from agent_riggs.store import Store
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class SessionBriefing:
|
|
14
|
+
trust_baseline: float | None
|
|
15
|
+
last_session: dict[str, Any] | None
|
|
16
|
+
known_issues: list[str]
|
|
17
|
+
active_candidates: int
|
|
18
|
+
|
|
19
|
+
def format(self):
|
|
20
|
+
lines = []
|
|
21
|
+
if self.trust_baseline is not None:
|
|
22
|
+
lines.append(f"Trust baseline: {self.trust_baseline:.2f}")
|
|
23
|
+
else:
|
|
24
|
+
lines.append("Trust baseline: no data")
|
|
25
|
+
if self.last_session:
|
|
26
|
+
s = self.last_session
|
|
27
|
+
lines.append(
|
|
28
|
+
f"Last session: {s['session_id']}, {s['total_turns']} turns, "
|
|
29
|
+
f"trust {s['trust_end']:.2f}"
|
|
30
|
+
)
|
|
31
|
+
else:
|
|
32
|
+
lines.append("Last session: none")
|
|
33
|
+
if self.known_issues:
|
|
34
|
+
lines.append("\nKnown issues:")
|
|
35
|
+
for issue in self.known_issues:
|
|
36
|
+
lines.append(f" - {issue}")
|
|
37
|
+
if self.active_candidates > 0:
|
|
38
|
+
lines.append(f"\nActive ratchet candidates: {self.active_candidates}")
|
|
39
|
+
return "\n".join(lines)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def generate_briefing(store: Store, project: str, config: RiggsConfig) -> SessionBriefing:
|
|
43
|
+
trust_row = store.execute(
|
|
44
|
+
"SELECT trust_15 FROM turns WHERE project = ? ORDER BY timestamp DESC LIMIT 1",
|
|
45
|
+
[project],
|
|
46
|
+
).fetchone()
|
|
47
|
+
trust_baseline = trust_row[0] if trust_row else None
|
|
48
|
+
|
|
49
|
+
session_row = store.execute(
|
|
50
|
+
"""SELECT session_id, total_turns, total_failures, trust_end, CAST(ended_at AS VARCHAR)
|
|
51
|
+
FROM session_summaries WHERE project = ? ORDER BY ended_at DESC LIMIT 1""",
|
|
52
|
+
[project],
|
|
53
|
+
).fetchone()
|
|
54
|
+
last_session = None
|
|
55
|
+
if session_row:
|
|
56
|
+
last_session = {
|
|
57
|
+
"session_id": session_row[0],
|
|
58
|
+
"total_turns": session_row[1],
|
|
59
|
+
"total_failures": session_row[2],
|
|
60
|
+
"trust_end": session_row[3],
|
|
61
|
+
"ended_at": session_row[4],
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
failure_rows = store.execute(
|
|
65
|
+
"""SELECT failure_category, count(*) AS cnt FROM failure_stream
|
|
66
|
+
WHERE project = ? GROUP BY failure_category
|
|
67
|
+
HAVING count(*) >= 3 ORDER BY cnt DESC LIMIT 5""",
|
|
68
|
+
[project],
|
|
69
|
+
).fetchall()
|
|
70
|
+
known_issues = [f"{r[0]} ({r[1]} occurrences)" for r in failure_rows]
|
|
71
|
+
|
|
72
|
+
candidate_row = store.execute(
|
|
73
|
+
"""SELECT count(DISTINCT failure_category || coalesce(tool_name, '') || coalesce(mode, ''))
|
|
74
|
+
FROM failure_stream WHERE project = ? GROUP BY project HAVING count(*) >= ?""",
|
|
75
|
+
[project, config.ratchet.min_frequency],
|
|
76
|
+
).fetchone()
|
|
77
|
+
active_candidates = candidate_row[0] if candidate_row else 0
|
|
78
|
+
|
|
79
|
+
return SessionBriefing(
|
|
80
|
+
trust_baseline=trust_baseline,
|
|
81
|
+
last_session=last_session,
|
|
82
|
+
known_issues=known_issues,
|
|
83
|
+
active_candidates=active_candidates,
|
|
84
|
+
)
|
agent_riggs/cli.py
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""CLI entry point for agent-riggs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import shutil
|
|
6
|
+
from importlib import resources
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
from agent_riggs import __version__
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def find_project_root() -> Path:
|
|
15
|
+
"""Find the project root (cwd for now)."""
|
|
16
|
+
return Path.cwd()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@click.group()
|
|
20
|
+
@click.version_option(__version__, prog_name="agent-riggs")
|
|
21
|
+
@click.pass_context
|
|
22
|
+
def main(ctx: click.Context) -> None:
|
|
23
|
+
"""agent-riggs -- cross-session memory and analysis for the Rigged tool suite."""
|
|
24
|
+
ctx.ensure_object(dict)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@main.command()
|
|
28
|
+
def init() -> None:
|
|
29
|
+
"""Initialize .riggs/ directory and store."""
|
|
30
|
+
project_root = find_project_root()
|
|
31
|
+
riggs_dir = project_root / ".riggs"
|
|
32
|
+
riggs_dir.mkdir(exist_ok=True)
|
|
33
|
+
|
|
34
|
+
config_path = riggs_dir / "config.toml"
|
|
35
|
+
if not config_path.exists():
|
|
36
|
+
defaults_ref = resources.files("agent_riggs") / "defaults" / "config.toml"
|
|
37
|
+
config_path.write_text(defaults_ref.read_text(encoding="utf-8"))
|
|
38
|
+
|
|
39
|
+
from agent_riggs.assembly import assemble
|
|
40
|
+
|
|
41
|
+
service = assemble(project_root)
|
|
42
|
+
|
|
43
|
+
tools = []
|
|
44
|
+
for name in ("kibitzer", "blq", "jetsam", "fledgling"):
|
|
45
|
+
if shutil.which(name) or (project_root / f".{name}").exists():
|
|
46
|
+
tools.append(name)
|
|
47
|
+
|
|
48
|
+
click.echo(f"Initialized .riggs/ in {project_root}")
|
|
49
|
+
if tools:
|
|
50
|
+
click.echo(f"Discovered tools: {', '.join(tools)}")
|
|
51
|
+
else:
|
|
52
|
+
click.echo("No sibling tools discovered yet.")
|
|
53
|
+
|
|
54
|
+
service.store.close()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@main.command()
|
|
58
|
+
def ingest() -> None:
|
|
59
|
+
"""Ingest session data from sibling tools."""
|
|
60
|
+
from agent_riggs.assembly import assemble
|
|
61
|
+
|
|
62
|
+
project_root = find_project_root()
|
|
63
|
+
service = assemble(project_root)
|
|
64
|
+
|
|
65
|
+
ingest_plugin = service.plugin("ingest")
|
|
66
|
+
result = ingest_plugin.run()
|
|
67
|
+
|
|
68
|
+
click.echo(f"Ingested {result.turns_ingested} turns from {result.sources_read}")
|
|
69
|
+
if result.failures_recorded:
|
|
70
|
+
click.echo(f"Recorded {result.failures_recorded} failures")
|
|
71
|
+
|
|
72
|
+
service.store.close()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@main.command()
|
|
76
|
+
def status() -> None:
|
|
77
|
+
"""Show trust scores, mode, and ratchet summary."""
|
|
78
|
+
from agent_riggs.assembly import assemble
|
|
79
|
+
|
|
80
|
+
project_root = find_project_root()
|
|
81
|
+
service = assemble(project_root)
|
|
82
|
+
|
|
83
|
+
trust_plugin = service.plugin("trust")
|
|
84
|
+
data = trust_plugin.current()
|
|
85
|
+
|
|
86
|
+
if not data["has_data"]:
|
|
87
|
+
click.echo("trust: no data yet")
|
|
88
|
+
click.echo("\nRun `agent-riggs ingest` after a session.")
|
|
89
|
+
else:
|
|
90
|
+
click.echo(
|
|
91
|
+
f"trust: {data['trust_1']:.2f} / {data['trust_5']:.2f} / {data['trust_15']:.2f}"
|
|
92
|
+
)
|
|
93
|
+
click.echo(" now session baseline")
|
|
94
|
+
|
|
95
|
+
service.store.close()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# --- Trust commands ---
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@main.group("trust")
|
|
102
|
+
def trust_group() -> None:
|
|
103
|
+
"""Trust score commands."""
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@trust_group.command("current")
|
|
107
|
+
def trust_current() -> None:
|
|
108
|
+
"""Show current trust scores."""
|
|
109
|
+
from agent_riggs.assembly import assemble
|
|
110
|
+
|
|
111
|
+
service = assemble(find_project_root())
|
|
112
|
+
data = service.plugin("trust").current()
|
|
113
|
+
if not data["has_data"]:
|
|
114
|
+
click.echo("No trust data yet.")
|
|
115
|
+
else:
|
|
116
|
+
click.echo(f"trust_1 (now): {data['trust_1']:.4f}")
|
|
117
|
+
click.echo(f"trust_5 (session): {data['trust_5']:.4f}")
|
|
118
|
+
click.echo(f"trust_15 (baseline):{data['trust_15']:.4f}")
|
|
119
|
+
service.store.close()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@trust_group.command("history")
|
|
123
|
+
@click.option("--limit", default=20, help="Number of entries")
|
|
124
|
+
def trust_history(limit: int) -> None:
|
|
125
|
+
"""Show trust score history."""
|
|
126
|
+
from agent_riggs.assembly import assemble
|
|
127
|
+
|
|
128
|
+
service = assemble(find_project_root())
|
|
129
|
+
history = service.plugin("trust").history(limit=limit)
|
|
130
|
+
if not history:
|
|
131
|
+
click.echo("No trust history.")
|
|
132
|
+
else:
|
|
133
|
+
for entry in history:
|
|
134
|
+
click.echo(
|
|
135
|
+
f" [{entry['session_id']}:{entry['turn_number']}] "
|
|
136
|
+
f"{entry['trust_1']:.2f} / {entry['trust_5']:.2f} / {entry['trust_15']:.2f}"
|
|
137
|
+
)
|
|
138
|
+
service.store.close()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# --- Ratchet commands ---
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@main.group("ratchet")
|
|
145
|
+
def ratchet_group() -> None:
|
|
146
|
+
"""Ratchet candidate commands."""
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@ratchet_group.command("candidates")
|
|
150
|
+
def ratchet_candidates() -> None:
|
|
151
|
+
"""Show pending ratchet candidates."""
|
|
152
|
+
from agent_riggs.assembly import assemble
|
|
153
|
+
|
|
154
|
+
service = assemble(find_project_root())
|
|
155
|
+
candidates = service.plugin("ratchet").candidates()
|
|
156
|
+
if not candidates:
|
|
157
|
+
click.echo("No ratchet candidates.")
|
|
158
|
+
else:
|
|
159
|
+
for c in candidates:
|
|
160
|
+
click.echo(f"\n [{c.candidate_type}] {c.candidate_key}")
|
|
161
|
+
click.echo(f" {c.recommendation}")
|
|
162
|
+
click.echo(f" Evidence: {c.evidence}")
|
|
163
|
+
service.store.close()
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@ratchet_group.command("promote")
|
|
167
|
+
@click.argument("key")
|
|
168
|
+
@click.option("--reason", default=None, help="Reason for promotion")
|
|
169
|
+
def ratchet_promote(key: str, reason: str | None) -> None:
|
|
170
|
+
"""Promote a ratchet candidate."""
|
|
171
|
+
from agent_riggs.assembly import assemble
|
|
172
|
+
|
|
173
|
+
service = assemble(find_project_root())
|
|
174
|
+
try:
|
|
175
|
+
service.plugin("ratchet").promote(key, reason)
|
|
176
|
+
click.echo(f"Promoted: {key}")
|
|
177
|
+
except KeyError:
|
|
178
|
+
click.echo(f"No candidate with key: {key}", err=True)
|
|
179
|
+
service.store.close()
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@ratchet_group.command("reject")
|
|
183
|
+
@click.argument("key")
|
|
184
|
+
@click.option("--reason", required=True, help="Reason for rejection")
|
|
185
|
+
def ratchet_reject(key: str, reason: str) -> None:
|
|
186
|
+
"""Reject a ratchet candidate with reason."""
|
|
187
|
+
from agent_riggs.assembly import assemble
|
|
188
|
+
|
|
189
|
+
service = assemble(find_project_root())
|
|
190
|
+
try:
|
|
191
|
+
service.plugin("ratchet").reject(key, reason)
|
|
192
|
+
click.echo(f"Rejected: {key}")
|
|
193
|
+
except KeyError:
|
|
194
|
+
click.echo(f"No candidate with key: {key}", err=True)
|
|
195
|
+
service.store.close()
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@ratchet_group.command("history")
|
|
199
|
+
def ratchet_history() -> None:
|
|
200
|
+
"""Show ratchet decision history."""
|
|
201
|
+
from agent_riggs.assembly import assemble
|
|
202
|
+
|
|
203
|
+
service = assemble(find_project_root())
|
|
204
|
+
history = service.plugin("ratchet").history()
|
|
205
|
+
if not history:
|
|
206
|
+
click.echo("No ratchet decisions recorded.")
|
|
207
|
+
else:
|
|
208
|
+
for h in history:
|
|
209
|
+
click.echo(
|
|
210
|
+
f" [{h['decided_at']}] {h['decision']}: "
|
|
211
|
+
f"{h['candidate_key']} — {h.get('reason', '')}"
|
|
212
|
+
)
|
|
213
|
+
service.store.close()
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# --- Metrics command ---
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@main.command("metrics")
|
|
220
|
+
@click.option("--period", default=None, type=int, help="Period in days")
|
|
221
|
+
def metrics_cmd(period: int | None) -> None:
|
|
222
|
+
"""Show ratchet metrics dashboard."""
|
|
223
|
+
from agent_riggs.assembly import assemble
|
|
224
|
+
|
|
225
|
+
service = assemble(find_project_root())
|
|
226
|
+
m = service.plugin("metrics").compute(period)
|
|
227
|
+
click.echo(f"RATCHET METRICS ({m.total_sessions} sessions)\n")
|
|
228
|
+
click.echo(f" Ratchet velocity: {m.ratchet_velocity} promotions")
|
|
229
|
+
click.echo(f" Self-service ratio: {m.structured_tool_fraction:.0%} structured")
|
|
230
|
+
click.echo(f" Computation channel %: {m.computation_channel_fraction:.0%}")
|
|
231
|
+
click.echo(
|
|
232
|
+
f" Trust trajectory: {m.trust_trajectory_start:.2f} -> "
|
|
233
|
+
f"{m.trust_trajectory_end:.2f}"
|
|
234
|
+
)
|
|
235
|
+
click.echo(f" Failure rate: {m.failure_rate:.0%}")
|
|
236
|
+
if m.mode_distribution:
|
|
237
|
+
click.echo("\n Mode distribution:")
|
|
238
|
+
for mode, frac in sorted(m.mode_distribution.items(), key=lambda x: -x[1]):
|
|
239
|
+
click.echo(f" {mode}: {frac:.0%}")
|
|
240
|
+
service.store.close()
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# --- Brief command ---
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@main.command("brief")
|
|
247
|
+
def brief_cmd() -> None:
|
|
248
|
+
"""Full session briefing."""
|
|
249
|
+
from agent_riggs.assembly import assemble
|
|
250
|
+
|
|
251
|
+
service = assemble(find_project_root())
|
|
252
|
+
briefing = service.plugin("briefing").brief()
|
|
253
|
+
click.echo(f"PROJECT BRIEFING: {find_project_root().name}\n")
|
|
254
|
+
click.echo(briefing.format())
|
|
255
|
+
service.store.close()
|
agent_riggs/config.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Configuration loading: merge defaults with .riggs/config.toml."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import tomllib
|
|
6
|
+
from dataclasses import dataclass, field, fields
|
|
7
|
+
from importlib import resources
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class TrustConfig:
|
|
14
|
+
score_success: float = 1.0
|
|
15
|
+
score_suboptimal: float = 0.7
|
|
16
|
+
score_mode_switch_agent: float = 0.8
|
|
17
|
+
score_mode_switch_controller: float = 0.3
|
|
18
|
+
score_failure: float = 0.2
|
|
19
|
+
score_path_denial: float = 0.1
|
|
20
|
+
score_repeated_failure: float = 0.0
|
|
21
|
+
alpha_short: float = 0.4
|
|
22
|
+
alpha_session: float = 0.08
|
|
23
|
+
alpha_baseline: float = 0.02
|
|
24
|
+
tighten_threshold: float = 0.3
|
|
25
|
+
auto_tighten_threshold: float = 0.5
|
|
26
|
+
loosen_threshold: float = 0.9
|
|
27
|
+
loosen_sustained_turns: int = 20
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class RatchetConfig:
|
|
32
|
+
min_frequency: int = 5
|
|
33
|
+
min_sessions: int = 3
|
|
34
|
+
min_success_rate: float = 0.8
|
|
35
|
+
lookback_days: int = 30
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class SandboxConfig:
|
|
40
|
+
memory_headroom: float = 2.0
|
|
41
|
+
timeout_headroom: float = 3.0
|
|
42
|
+
cpu_headroom: float = 2.0
|
|
43
|
+
min_runs_for_tightening: int = 5
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class MetricsConfig:
|
|
48
|
+
default_period_days: int = 30
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class StoreConfig:
|
|
53
|
+
path: str = ".riggs/store.duckdb"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class RiggsConfig:
|
|
58
|
+
trust: TrustConfig = field(default_factory=TrustConfig)
|
|
59
|
+
ratchet: RatchetConfig = field(default_factory=RatchetConfig)
|
|
60
|
+
sandbox: SandboxConfig = field(default_factory=SandboxConfig)
|
|
61
|
+
metrics: MetricsConfig = field(default_factory=MetricsConfig)
|
|
62
|
+
store: StoreConfig = field(default_factory=StoreConfig)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _load_defaults() -> dict[str, Any]:
|
|
66
|
+
"""Load the shipped defaults/config.toml."""
|
|
67
|
+
defaults_ref = resources.files("agent_riggs") / "defaults" / "config.toml"
|
|
68
|
+
return tomllib.loads(defaults_ref.read_text(encoding="utf-8"))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
|
|
72
|
+
"""Merge override into base, recursing into nested dicts."""
|
|
73
|
+
merged = dict(base)
|
|
74
|
+
for key, value in override.items():
|
|
75
|
+
if key in merged and isinstance(merged[key], dict) and isinstance(value, dict):
|
|
76
|
+
merged[key] = _deep_merge(merged[key], value)
|
|
77
|
+
else:
|
|
78
|
+
merged[key] = value
|
|
79
|
+
return merged
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _dict_to_dataclass(cls: type, data: dict[str, Any]) -> Any:
|
|
83
|
+
"""Convert a dict to a dataclass, ignoring unknown keys."""
|
|
84
|
+
known = {f.name for f in fields(cls)}
|
|
85
|
+
return cls(**{k: v for k, v in data.items() if k in known})
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def load_config(project_root: Path) -> RiggsConfig:
|
|
89
|
+
"""Load config: defaults merged with .riggs/config.toml."""
|
|
90
|
+
merged = _load_defaults()
|
|
91
|
+
|
|
92
|
+
user_path = project_root / ".riggs" / "config.toml"
|
|
93
|
+
if user_path.exists():
|
|
94
|
+
user = tomllib.loads(user_path.read_text(encoding="utf-8"))
|
|
95
|
+
merged = _deep_merge(merged, user)
|
|
96
|
+
|
|
97
|
+
return RiggsConfig(
|
|
98
|
+
trust=_dict_to_dataclass(TrustConfig, merged.get("trust", {})),
|
|
99
|
+
ratchet=_dict_to_dataclass(RatchetConfig, merged.get("ratchet", {})),
|
|
100
|
+
sandbox=_dict_to_dataclass(SandboxConfig, merged.get("sandbox", {})),
|
|
101
|
+
metrics=_dict_to_dataclass(MetricsConfig, merged.get("metrics", {})),
|
|
102
|
+
store=_dict_to_dataclass(StoreConfig, merged.get("store", {})),
|
|
103
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[trust]
|
|
2
|
+
score_success = 1.0
|
|
3
|
+
score_suboptimal = 0.7
|
|
4
|
+
score_mode_switch_agent = 0.8
|
|
5
|
+
score_mode_switch_controller = 0.3
|
|
6
|
+
score_failure = 0.2
|
|
7
|
+
score_path_denial = 0.1
|
|
8
|
+
score_repeated_failure = 0.0
|
|
9
|
+
|
|
10
|
+
alpha_short = 0.4
|
|
11
|
+
alpha_session = 0.08
|
|
12
|
+
alpha_baseline = 0.02
|
|
13
|
+
|
|
14
|
+
tighten_threshold = 0.3
|
|
15
|
+
auto_tighten_threshold = 0.5
|
|
16
|
+
loosen_threshold = 0.9
|
|
17
|
+
loosen_sustained_turns = 20
|
|
18
|
+
|
|
19
|
+
[ratchet]
|
|
20
|
+
min_frequency = 5
|
|
21
|
+
min_sessions = 3
|
|
22
|
+
min_success_rate = 0.8
|
|
23
|
+
lookback_days = 30
|
|
24
|
+
|
|
25
|
+
[sandbox]
|
|
26
|
+
memory_headroom = 2.0
|
|
27
|
+
timeout_headroom = 3.0
|
|
28
|
+
cpu_headroom = 2.0
|
|
29
|
+
min_runs_for_tightening = 5
|
|
30
|
+
|
|
31
|
+
[metrics]
|
|
32
|
+
default_period_days = 30
|
|
33
|
+
|
|
34
|
+
[store]
|
|
35
|
+
path = ".riggs/store.duckdb"
|
|
File without changes
|