sbxloop 0.7.3__tar.gz → 0.7.4__tar.gz

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 (66) hide show
  1. {sbxloop-0.7.3 → sbxloop-0.7.4}/PKG-INFO +1 -1
  2. sbxloop-0.7.3/src/sbxloop/_vendor/sbxloop_worker-0.7.3-py3-none-any.whl → sbxloop-0.7.4/src/sbxloop/_vendor/sbxloop_worker-0.7.4-py3-none-any.whl +0 -0
  3. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/_version.py +2 -2
  4. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/audits.py +29 -10
  5. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/loop.py +11 -0
  6. {sbxloop-0.7.3 → sbxloop-0.7.4}/.gitignore +0 -0
  7. {sbxloop-0.7.3 → sbxloop-0.7.4}/README.md +0 -0
  8. {sbxloop-0.7.3 → sbxloop-0.7.4}/hatch_build.py +0 -0
  9. {sbxloop-0.7.3 → sbxloop-0.7.4}/pyproject.toml +0 -0
  10. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/__init__.py +0 -0
  11. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/cli/__init__.py +0 -0
  12. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/cli/app.py +0 -0
  13. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/cli/doctor.py +0 -0
  14. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/cli/models.py +0 -0
  15. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/cli/tui.py +0 -0
  16. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/config.py +0 -0
  17. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/__init__.py +0 -0
  18. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/backlog.py +0 -0
  19. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/control.py +0 -0
  20. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/discord.py +0 -0
  21. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/discord_format.py +0 -0
  22. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/github.py +0 -0
  23. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/model.py +0 -0
  24. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/paths.py +0 -0
  25. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/postmortem.py +0 -0
  26. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/sources.py +0 -0
  27. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/daemon/store.py +0 -0
  28. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/deliver.py +0 -0
  29. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/engine/__init__.py +0 -0
  30. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/engine/engine.py +0 -0
  31. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/engine/model.py +0 -0
  32. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/engine/phases.py +0 -0
  33. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/engine/prompts/__init__.py +0 -0
  34. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/engine/prompts/decompose.md +0 -0
  35. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/engine/prompts/execute.md +0 -0
  36. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/engine/prompts/plan.md +0 -0
  37. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/engine/prompts/scrutinize.md +0 -0
  38. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/engine/prompts/steer.md +0 -0
  39. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/engine/prompts/validate.md +0 -0
  40. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/engine/store.py +0 -0
  41. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/errors.py +0 -0
  42. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/events.py +0 -0
  43. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/gc.py +0 -0
  44. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/gh/__init__.py +0 -0
  45. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/gh/ops.py +0 -0
  46. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/gh/reporter.py +0 -0
  47. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/hostgit.py +0 -0
  48. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/ids.py +0 -0
  49. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/policy.py +0 -0
  50. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/py.typed +0 -0
  51. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/sbx/__init__.py +0 -0
  52. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/sbx/bake.py +0 -0
  53. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/sbx/cli.py +0 -0
  54. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/sbx/conformance.py +0 -0
  55. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/sbx/models.py +0 -0
  56. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/sbx/pair.py +0 -0
  57. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/sbx/parse.py +0 -0
  58. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/sbx/provision.py +0 -0
  59. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/sbx/prune.py +0 -0
  60. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/sbx/sandbox.py +0 -0
  61. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/sbx/secretstate.py +0 -0
  62. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/toolchains.py +0 -0
  63. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/verifylint.py +0 -0
  64. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/worker/__init__.py +0 -0
  65. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/worker/client.py +0 -0
  66. {sbxloop-0.7.3 → sbxloop-0.7.4}/src/sbxloop/worker/wheel.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sbxloop
3
- Version: 0.7.3
3
+ Version: 0.7.4
4
4
  Summary: Agentic loop orchestration on Docker Sandboxes (sbx) with isolated credential domains
5
5
  Project-URL: Homepage, https://github.com/brettbergin/sbxloop
6
6
  Project-URL: Repository, https://github.com/brettbergin/sbxloop
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.7.3'
22
- __version_tuple__ = version_tuple = (0, 7, 3)
21
+ __version__ = version = '0.7.4'
22
+ __version_tuple__ = version_tuple = (0, 7, 4)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -21,6 +21,11 @@ from pathlib import Path
21
21
  AUDIT_DIR = Path(".github") / "sbxloop" / "audits"
22
22
  AUDIT_TITLE_PREFIX = "audit: "
23
23
  _FRONT_MATTER = re.compile(r"\A---\s*\n(.*?)\n---\s*\n?", re.S)
24
+ # Preferred metadata form: an HTML comment. Invisible on GitHub and left
25
+ # alone by every Markdown formatter (mdformat rewrote a `---` YAML block
26
+ # into a thematic break + an H2 the first time the charters shipped, and
27
+ # the daemon then saw no metadata at all).
28
+ _COMMENT_META = re.compile(r"\A\s*<!--\s*sbxloop(?:-audit)?\s*:?\s*(.*?)\s*-->\s*\n?", re.S)
24
29
  _EVERY = re.compile(r"^\s*(\d+(?:\.\d+)?)\s*([dhm])\s*$", re.I)
25
30
  _UNIT_S = {"d": 86_400.0, "h": 3_600.0, "m": 60.0}
26
31
  _NAME_OK = re.compile(r"^[a-z0-9][a-z0-9._-]{0,63}$")
@@ -49,21 +54,35 @@ def parse_every(text: str) -> float:
49
54
 
50
55
 
51
56
  def parse_charter(path: Path, rel: str | None = None) -> Charter:
52
- """One charter file. Front-matter keys: ``every`` (required), ``enabled``
53
- (default true). The name is the file stem, which also keys the schedule
54
- and the issue title — keep it stable."""
57
+ """One charter file. Metadata keys: ``every`` (required), ``enabled``
58
+ (default true), given as ``<!-- sbxloop: every=7d -->`` on the first line
59
+ (preferred) or a ``---`` front-matter block. The name is the file stem,
60
+ which also keys the schedule and the issue title — keep it stable."""
55
61
  name = path.stem
56
62
  if not _NAME_OK.match(name):
57
63
  raise ValueError(f"charter file name {name!r} must be lowercase [a-z0-9._-]")
58
64
  text = path.read_text(encoding="utf-8")
59
- m = _FRONT_MATTER.match(text)
60
- if not m:
61
- raise ValueError(f"{path}: missing front-matter (--- every: 7d ---)")
62
65
  meta: dict[str, str] = {}
63
- for line in m.group(1).splitlines():
64
- if ":" in line:
65
- key, _, value = line.partition(":")
66
- meta[key.strip().lower()] = value.strip().strip("'\"")
66
+ m = _COMMENT_META.match(text)
67
+ if m:
68
+ # `<!-- sbxloop: every=7d enabled=false -->` (also `key: value`)
69
+ for token in re.split(r"[\s,;]+", m.group(1).strip()):
70
+ if not token:
71
+ continue
72
+ key, sep, value = token.partition("=") if "=" in token else token.partition(":")
73
+ if sep:
74
+ meta[key.strip().lower()] = value.strip().strip("'\"")
75
+ else:
76
+ m = _FRONT_MATTER.match(text)
77
+ if not m:
78
+ raise ValueError(
79
+ f"{path}: missing metadata — start the file with "
80
+ "`<!-- sbxloop: every=7d -->` (or a --- every: 7d --- front-matter block)"
81
+ )
82
+ for line in m.group(1).splitlines():
83
+ if ":" in line:
84
+ key, _, value = line.partition(":")
85
+ meta[key.strip().lower()] = value.strip().strip("'\"")
67
86
  if "every" not in meta:
68
87
  raise ValueError(f"{path}: front-matter needs `every: <interval>`")
69
88
  body = text[m.end() :].strip()
@@ -57,6 +57,9 @@ from sbxloop.sbx.prune import remove_run_sandbox, remove_run_sandbox_secrets
57
57
 
58
58
  logger = logging.getLogger(__name__)
59
59
 
60
+ # How often the audit scheduler fast-forwards the checkout to see new charters.
61
+ AUDIT_REFRESH_S = 600.0
62
+
60
63
 
61
64
  class Frontend(Protocol):
62
65
  """What a human-facing channel (Discord) sees of the loop's lifecycle.
@@ -127,6 +130,7 @@ class DaemonLoop:
127
130
  self._stop = threading.Event()
128
131
  self._paused = False
129
132
  self._audit_problems_seen: set[str] = set()
133
+ self._last_audit_refresh = float("-inf")
130
134
  self._current: RunHandle | None = None
131
135
  self._current_lock = threading.Lock()
132
136
  self._cancel_request: CancelRequest | None = None
@@ -894,6 +898,13 @@ class DaemonLoop:
894
898
  checkout = self._workspace_checkout()
895
899
  if checkout is None:
896
900
  return
901
+ # The checkout is normally refreshed only when a run starts, so
902
+ # charters merged after the last run would never be seen (field:
903
+ # the first deploy read a clone that predated its own charters).
904
+ # Refresh here too, throttled — a fetch a minute is not the point.
905
+ if now - self._last_audit_refresh >= AUDIT_REFRESH_S:
906
+ self._last_audit_refresh = now
907
+ self._refresh_workspace()
897
908
  charters, problems = load_charters(checkout, daemon.audit_dir)
898
909
  for problem in problems:
899
910
  if problem not in self._audit_problems_seen:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes