subtask-mcp 0.2.4__tar.gz → 0.2.6__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.
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/PKG-INFO +2 -2
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/README.md +1 -1
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/__init__.py +1 -1
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/worker.py +7 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask_mcp.egg-info/PKG-INFO +2 -2
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/tests/test_concurrency.py +24 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/tests/test_routing_results.py +11 -4
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/LICENSE +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/pyproject.toml +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/setup.cfg +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/admission.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/capacity.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/cli.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/config.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/core.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/doctor.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/engines/__init__.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/engines/base.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/engines/claude_api.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/engines/codex.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/engines/demo.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/engines/dsh.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/engines/kimi.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/engines/opencode.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/engines/registry.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/events.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/evidence.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/fastserver.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/gc.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/install/__init__.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/install/claude_plugin.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/install/clients.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/install/commands.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/install/doctor.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/install/fileops.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/install/installer.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/install/ownership.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/install/tomledit.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/jobs.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/ledger.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/narrate.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/policy.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/procid.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/progress.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/redact.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/roi.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/router.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/runners.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/schemacheck.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/server.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/skill/SKILL.md +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/telemetry.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask/tools.json +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask_mcp.egg-info/SOURCES.txt +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask_mcp.egg-info/dependency_links.txt +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask_mcp.egg-info/entry_points.txt +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask_mcp.egg-info/requires.txt +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/subtask_mcp.egg-info/top_level.txt +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/tests/test_engines.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/tests/test_falsify.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/tests/test_independent_client.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/tests/test_install.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/tests/test_protocol.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/tests/test_schema.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/tests/test_security.py +0 -0
- {subtask_mcp-0.2.4 → subtask_mcp-0.2.6}/tests/test_subtask.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: subtask-mcp
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Background sub-agents for Claude Code, Codex, Kimi and OpenCode, over MCP or CLI, with a spend cap
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/tedin7/subtask
|
|
@@ -70,7 +70,7 @@ The distribution is `subtask-mcp` (the PyPI name `subtask` is an unrelated
|
|
|
70
70
|
project). Full guide, per client, with native commands: [docs/install.md](docs/install.md).
|
|
71
71
|
|
|
72
72
|
```sh
|
|
73
|
-
uv tool install "git+https://github.com/tedin7/subtask@v0.2.
|
|
73
|
+
uv tool install "git+https://github.com/tedin7/subtask@v0.2.6" # or: uv tool install /path/to/checkout
|
|
74
74
|
subtask install --client all # prints the plan for every detected client
|
|
75
75
|
subtask install --client all --yes # applies it: MCP server + skill
|
|
76
76
|
subtask doctor --client all # checks app, config entry and a real handshake
|
|
@@ -39,7 +39,7 @@ The distribution is `subtask-mcp` (the PyPI name `subtask` is an unrelated
|
|
|
39
39
|
project). Full guide, per client, with native commands: [docs/install.md](docs/install.md).
|
|
40
40
|
|
|
41
41
|
```sh
|
|
42
|
-
uv tool install "git+https://github.com/tedin7/subtask@v0.2.
|
|
42
|
+
uv tool install "git+https://github.com/tedin7/subtask@v0.2.6" # or: uv tool install /path/to/checkout
|
|
43
43
|
subtask install --client all # prints the plan for every detected client
|
|
44
44
|
subtask install --client all --yes # applies it: MCP server + skill
|
|
45
45
|
subtask doctor --client all # checks app, config entry and a real handshake
|
|
@@ -147,6 +147,13 @@ def main() -> int:
|
|
|
147
147
|
error=None,
|
|
148
148
|
)
|
|
149
149
|
emit("start", f"{spec['engine']} attempt {attempt}", state="running")
|
|
150
|
+
if adapter.capabilities.paid and not rid:
|
|
151
|
+
# Started by an older server that left the booking to the worker (v0 jobs):
|
|
152
|
+
# reserve here, so the spend still counts against the monthly cap.
|
|
153
|
+
rid = f"{job_id}#w{attempt}-{os.getpid()}"
|
|
154
|
+
if ledger.reserve(rid, job_id, float(spec["max_usd"])) is None:
|
|
155
|
+
rid = None
|
|
156
|
+
raise RuntimeError(f"monthly cap {config.MONTHLY_CAP_USD} $ reached")
|
|
150
157
|
if not Path(spec["cwd"]).is_dir():
|
|
151
158
|
raise RuntimeError(f"working folder is gone: {spec['cwd']} (cleaned up?)")
|
|
152
159
|
level = (spec.get("policy") or {}).get("effective")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: subtask-mcp
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Background sub-agents for Claude Code, Codex, Kimi and OpenCode, over MCP or CLI, with a spend cap
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/tedin7/subtask
|
|
@@ -70,7 +70,7 @@ The distribution is `subtask-mcp` (the PyPI name `subtask` is an unrelated
|
|
|
70
70
|
project). Full guide, per client, with native commands: [docs/install.md](docs/install.md).
|
|
71
71
|
|
|
72
72
|
```sh
|
|
73
|
-
uv tool install "git+https://github.com/tedin7/subtask@v0.2.
|
|
73
|
+
uv tool install "git+https://github.com/tedin7/subtask@v0.2.6" # or: uv tool install /path/to/checkout
|
|
74
74
|
subtask install --client all # prints the plan for every detected client
|
|
75
75
|
subtask install --client all --yes # applies it: MCP server + skill
|
|
76
76
|
subtask doctor --client all # checks app, config entry and a real handshake
|
|
@@ -273,3 +273,27 @@ def test_missing_active_index_does_not_forget_running_jobs(home):
|
|
|
273
273
|
assert sorted(admission.active()) == sorted([first, second])
|
|
274
274
|
core.stop(first)
|
|
275
275
|
core.stop(second)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def test_job_started_by_an_old_server_still_books_its_spend(home, monkeypatch):
|
|
279
|
+
"""v0 spec/status (no attempts, no reservation): the worker reserves and settles itself."""
|
|
280
|
+
import signal
|
|
281
|
+
|
|
282
|
+
from subtask import jobs, ledger, worker
|
|
283
|
+
|
|
284
|
+
job = "st-v0v0v0v0v0"
|
|
285
|
+
folder = jobs.job_dir(job)
|
|
286
|
+
folder.mkdir(parents=True)
|
|
287
|
+
jobs.write_json(folder / "spec.json", {"task": "x", "engine": "fake", "cwd": str(home), "max_usd": 2.0,
|
|
288
|
+
"max_turns": 5, "effort": "low", "created": time.time()}) # fmt: skip
|
|
289
|
+
jobs.write_json(folder / "status.json", {"state": "queued"})
|
|
290
|
+
monkeypatch.setenv("SUBTASK_FAKE_SCRIPT", 'echo \'{"say":"ok"}\'')
|
|
291
|
+
monkeypatch.setattr(sys, "argv", ["worker", job])
|
|
292
|
+
old = signal.getsignal(signal.SIGTERM)
|
|
293
|
+
try:
|
|
294
|
+
worker.main()
|
|
295
|
+
finally:
|
|
296
|
+
signal.signal(signal.SIGTERM, old)
|
|
297
|
+
assert jobs.status(job)["state"] == "done"
|
|
298
|
+
assert ledger.committed() == pytest.approx(0.25)
|
|
299
|
+
assert ledger.open_for(job) == []
|
|
@@ -196,16 +196,23 @@ def test_doctor_reports_without_spending(home):
|
|
|
196
196
|
|
|
197
197
|
|
|
198
198
|
def test_active_index_makes_counting_independent_of_history(home):
|
|
199
|
+
"""Counting must not grow with finished jobs: compare with an empty history, not a fixed time."""
|
|
199
200
|
from subtask import admission, config, jobs
|
|
200
201
|
|
|
202
|
+
def per_call() -> float:
|
|
203
|
+
t = time.perf_counter()
|
|
204
|
+
for _ in range(20):
|
|
205
|
+
admission.count()
|
|
206
|
+
return (time.perf_counter() - t) / 20
|
|
207
|
+
|
|
208
|
+
admission.count()
|
|
209
|
+
empty = per_call()
|
|
201
210
|
for i in range(300):
|
|
202
211
|
folder = config.JOBS / f"st-old{i:05d}"
|
|
203
212
|
folder.mkdir(parents=True)
|
|
204
213
|
jobs.write_json(folder / "status.json", {"state": "done"})
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
admission.count()
|
|
208
|
-
assert (time.perf_counter() - t) / 20 < 0.02 # does not scan 300 finished jobs
|
|
214
|
+
full = per_call()
|
|
215
|
+
assert full < empty * 3 + 0.005, (empty, full) # O(history) would be ~300x
|
|
209
216
|
|
|
210
217
|
|
|
211
218
|
def test_narrator_context_includes_observed_activity_not_only_intentions(home):
|
|
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
|
|
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
|
|
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
|