Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
146 commits
Select commit Hold shift + click to select a range
59369f0
feat(threadkeeper): configurable hybrid OmegaClaw mesh for budget-awa…
Jun 17, 2026
2aba7d9
feat(demo): live 4-engine mesh dashboard, bulk-worker demo, DR docs
Jun 26, 2026
d31be06
docs: add RSI demonstration + transcript; fix upstream-sync wording
Jun 27, 2026
a108a0a
demo: add escalation-demo.md — honest smart-routing demo (easy->local…
Jun 27, 2026
4d8fdfb
docs: add pitch-artifacts-slide.md — verified artifact list for the 3…
Jun 27, 2026
7abc2a2
dashboard: chat-first UX + honest, persistent counters
Jun 28, 2026
4f71311
dashboard: dynamic Primary tile + current-config local engines + full…
Jun 28, 2026
b7ef64a
Lift the escalation policy from Python into MeTTa (Atomspace rules)
Jun 28, 2026
46f926b
Wire should_escalate into the live delegate path (cloud-gating)
Jun 28, 2026
a64de99
README: honest scope note on what the dashboard numbers mean
Jun 28, 2026
3a870c5
Harden subagent safety boundaries
Jun 30, 2026
f79891c
Harden subagent calls and file writes
Jun 30, 2026
7368da6
Persist structured subagent run records
Jul 1, 2026
e0b038d
Add subagent quotas and escalation integrity
Jul 1, 2026
68d7bc5
Add subagent task contracts
Jul 1, 2026
f6df5ef
Make subagent append writes atomic
Jul 1, 2026
0b185a4
Require explicit subagent provider metadata
Jul 1, 2026
2fcb0ba
Pin subagent persona integrity
Jul 1, 2026
5fdd131
Harden subagent persona examples
Jul 1, 2026
9b6439c
Add subagent LLM rate limit guard
Jul 1, 2026
07c8742
Add subagent LLM concurrency guard
Jul 1, 2026
34c96b4
Lock subagent file writes
Jul 1, 2026
5f33c8b
Harden subagent emit protocol
Jul 1, 2026
8b35a91
Validate subagent task contracts
Jul 1, 2026
a4a9b87
Tighten subagent task contract validation
Jul 2, 2026
970b519
Record subagent setup failures
Jul 2, 2026
caf3f9b
Structure no-tool subagent setup errors
Jul 2, 2026
f1a8a70
Sandbox subagent persona prompts
Jul 2, 2026
ca872e4
Skip Docker cleanup when unavailable in mock pytest gate
Jul 2, 2026
0d1d0af
Add subagent transcript checksums
Jul 2, 2026
1d30b4b
Allow task contracts to narrow subagent tool quotas
Jul 2, 2026
d8b922f
Index subagent run records
Jul 2, 2026
c3e836b
Fail closed on missing subagent cloud client
Jul 2, 2026
24bf6bf
Harden subagent numeric env parsing
Jul 2, 2026
893a0e3
Run subagent shell commands in workspace
Jul 2, 2026
8e12b18
Restrict subagent shell executable paths
Jul 2, 2026
01fe0d8
Sanitize subagent shell PATH
Jul 2, 2026
b828ebf
Scrub subagent shell environment
Jul 3, 2026
94d57c9
Cap subagent tool calls per turn
Jul 3, 2026
f880c50
Bound subagent read-file output
Jul 3, 2026
554fb85
Add dispatch wall-clock timeout and worker token accounting
Jul 3, 2026
d0c887d
Hash-chain subagent run index
Jul 3, 2026
f77ac1b
Add subagent patch proposal mode
Jul 3, 2026
a33b1e3
Add queue-only subagent dispatch mode
Jul 3, 2026
09899a0
Add optional adjudicator gate for high-stakes subagent outputs
Jul 3, 2026
1c001e4
Document requires_adjudication task contract in subagent reference
Jul 3, 2026
8eae787
Add queued subagent worker primitive
Jul 3, 2026
ec17402
Add bounded queued dispatch drain helper
Jul 3, 2026
5a472cd
Retain failed queued subagent claims
Jul 3, 2026
38ae193
Ignore queue result sidecars when draining
Jul 3, 2026
ee883ce
Add subagent candidate review helper
Jul 3, 2026
b14ade5
Add subagent run index verifier
Jul 4, 2026
d0d1dfa
Reject queue result sidecars as worker tasks
Jul 4, 2026
76bd2c4
Preserve queued subagent task contracts
Jul 4, 2026
f916dfe
Verify queued subagent task checksums
Jul 4, 2026
30adb54
Bound optional subagent shell argv
Jul 4, 2026
0cde0c7
Bound subagent shell output controls
Jul 4, 2026
7a1866d
Validate queued subagent task schema
Jul 4, 2026
7628f49
Tighten queued task contract schema
Jul 4, 2026
ac7dfd4
Reject coerced queued task integers
Jul 4, 2026
c041d24
Preserve queued subagent cancellation token
Jul 4, 2026
cc3cd1e
Add bounded queued worker loop
Jul 4, 2026
90f42f5
Add queued worker loop lock metadata
Jul 4, 2026
c4ae8a8
Add bounded subagent worker loop runner
Jul 4, 2026
bb6c8c7
Add worker loop runtime-cap and error-continuation tests
Jul 4, 2026
61bce9c
Validate worker loop stop file config
Jul 5, 2026
10f69bf
Validate worker loop explicit bounds
Jul 5, 2026
4812345
Add artifact-local worker loop smoke
Jul 5, 2026
120d689
Load env files in worker loop runner
Jul 5, 2026
9e9dda2
Reject unsafe worker env-file keys
Jul 5, 2026
85145ea
Bound worker env file parsing
Jul 5, 2026
d9b9b56
Add max_consecutive_errors to worker loop
Jul 5, 2026
225d441
Bound worker loop results and add live lock metadata
Jul 5, 2026
ec96332
Bound transcript turn records and add retry backoff jitter
Jul 5, 2026
c7beaf0
Reject expired queued tasks before worker LLM calls
Jul 5, 2026
14ec2ee
Add dispatch-level token budget cap
Jul 6, 2026
c8a14e4
Add graceful SIGTERM/SIGINT handling to worker loop
Jul 6, 2026
25483fb
Sanitize absolute paths from tool error messages
Jul 6, 2026
6bbf76d
Detect stale worker lock from crashed previous worker
Jul 6, 2026
41f863c
Add current-task tracking to worker loop lock metadata
Jul 6, 2026
1f4d70b
Add completion fields and queue depth to worker loop lock metadata
Jul 6, 2026
1c6c0c6
Add per-task duration and total runtime to worker loop results
Jul 6, 2026
fb60ebf
Bound transcript summary field and reject empty shell commands
Jul 6, 2026
ca98d36
Add run index entry bounding with rotation
Jul 6, 2026
86cc243
Reject empty search/tavily-search/technical-analysis queries
Jul 6, 2026
ee894d3
Add output size cap for search/tavily-search/technical-analysis tools
Jul 6, 2026
c6f9708
Add workspace file size cap for write-file and append-file
Jul 7, 2026
209da6c
Make JSON audit read cap configurable
Jul 7, 2026
4d7d4f4
Bound final subagent emit size
Jul 7, 2026
2f0a10b
Reject non-finite numeric env knobs
Jul 7, 2026
fc34757
Bound raw subagent worker responses
Jul 7, 2026
080ed28
Bound subagent run index audits
Jul 7, 2026
ddf9aae
Bound native worker HTTP responses
Jul 7, 2026
a258c73
Bound transcript hash audit reads
Jul 7, 2026
82f490b
Bound run index append tail reads
Jul 7, 2026
25435f2
Require string subagent tool arguments
Jul 7, 2026
52a9bc9
Require string final emits
Jul 7, 2026
3f94350
Confine worker control token paths
Jul 7, 2026
c784765
Bound patch proposal transcript content
Jul 8, 2026
d29f462
Bound shell output memory capture
Jul 8, 2026
77197df
Bound checksum sidecar reads
Jul 8, 2026
ef89b40
Bound candidate review sidecar reads
Jul 8, 2026
313664a
Bound escalation policy integrity reads
Jul 8, 2026
6e790ba
Bound persona setup reads
Jul 8, 2026
63bc0bc
Bound worker LLM state reads
Jul 8, 2026
062ee72
Stream transcript audit hashing
Jul 8, 2026
76587eb
Bound worker lock metadata reads
Jul 8, 2026
db834ce
Reject symlink async worker locks
Jul 8, 2026
df483df
Reject symlink queued task records
Jul 8, 2026
9b6fb3c
Stop async worker at consecutive error cap
Jul 8, 2026
2258f6a
Clear async worker signal state after run
Jul 9, 2026
cbe37d3
Tighten inline task contract list validation
Jul 9, 2026
372e336
Reject symlink run index files
Jul 9, 2026
7387f40
Reject symlink integrity sidecars
Jul 9, 2026
6c281bc
Reject symlink workspace file locks
Jul 9, 2026
59140ac
Harden run index tail and rotation writes
Jul 9, 2026
3a31e25
Reject symlink LLM guard state
Jul 9, 2026
4c73526
Reject symlink transcript audit records
Jul 9, 2026
7f3a70e
Harden worker usage accounting writes
Jul 9, 2026
55160f7
Reject symlink escalation policies
Jul 9, 2026
c54d6ca
Reject symlink audit write targets
Jul 9, 2026
1220907
Use nofollow opens for audit reads
Jul 9, 2026
0f0b2cc
Reject symlink persona setup files
Jul 9, 2026
5b5c6d9
Harden budget audit log files
Jul 10, 2026
b9298dd
Harden budget config reads
Jul 10, 2026
78a15bc
Durably fsync ThreadKeeper audit writes
Jul 10, 2026
14d7f90
Harden workspace file tool reads with O_NOFOLLOW
Jul 10, 2026
b567ad9
Reject symlink audit parent directories
Jul 10, 2026
28adabf
Reject symlink workspace file parents
Jul 10, 2026
33d79a5
Reject trailing payloads after quoted emits
Jul 10, 2026
25d96c8
Harden worker usage log parent
Jul 10, 2026
aec4c9b
Use nofollow fd for append size checks
Jul 10, 2026
1cbc15b
Reject symlink audit ancestor directories
Jul 10, 2026
ff24af5
Reject symlink queued dispatch directories
Jul 10, 2026
2350afa
Reject symlink LLM guard state parents
Jul 10, 2026
23ebb66
Reject non-relative file tool paths
Jul 10, 2026
b424f9e
Use open fds for setup size checks
Jul 11, 2026
558c3dc
Harden task contract path validation
Jul 11, 2026
e7ae245
Bound run-index audit reads
Jul 11, 2026
4034064
Harden budget audit log parents
Jul 11, 2026
6a6916f
Avoid path getsize in transcript audits
Jul 11, 2026
bedfadb
Bound budget accounting reads after open
Jul 11, 2026
78c43f2
Validate persona config scalar fields
Jul 11, 2026
bf65398
Reject unquoted emit trailing payloads
Jul 11, 2026
b25d763
Reject symlink subagent workspaces
Jul 11, 2026
9b00e38
Reject control chars in subagent command args
Jul 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ repos/
# secrets
.env

# ThreadKeeper runtime logs (regenerated at runtime; not source)
memory/usage.jsonl
memory/escalations.jsonl

# venv
.venv

Expand All @@ -22,3 +26,5 @@ Autotests/logs/

# IDE
.idea/
*.env
telegram.env
4 changes: 4 additions & 0 deletions Autotests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
teardown ran (agent is autonomous and may produce `remember` calls with a
delay).
"""
import shutil
import time

import pytest
Expand All @@ -17,6 +18,9 @@
@pytest.fixture(scope="session", autouse=True)
def _post_session_cleanup():
yield
if shutil.which("docker") is None:
print("\n>> post-session cleanup skipped: docker not available", flush=True)
return
print("\n>> post-session cleanup (grace period 15s)", flush=True)
time.sleep(15)
h = history_cleanup_by_markers(LEGACY_MARKERS)
Expand Down
103 changes: 103 additions & 0 deletions Autotests/mock/run_worker_loop_one_task_smoke.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""Non-live one-task smoke for ThreadKeeper's queued worker loop.

This helper builds an artifact-local run directory and persona, queues exactly
one task through queue-only dispatch, monkeypatches the worker LLM call to a
local deterministic emit, and drains it with run_queued_worker_loop(...). It is
intended for staged install/supervisor validation without Telegram, secrets, or
paid/provider calls.
"""
import json
import os
from pathlib import Path
import sys
import tempfile

ROOT = Path(__file__).resolve().parents[2]
SRC = ROOT / "src"
if str(SRC) not in sys.path:
sys.path.insert(0, str(SRC))

import subagent # noqa: E402


def _write_smoke_persona(base: Path) -> Path:
persona_dir = base / "personas"
persona_dir.mkdir(parents=True, exist_ok=True)
(persona_dir / "artifact-smoke.txt").write_text(
"You are a deterministic artifact-local smoke subagent. Emit concise success.",
encoding="utf-8",
)
(persona_dir / "artifact-smoke.json").write_text(json.dumps({
"persona_file": "artifact-smoke.txt",
"provider": "ollama",
"model": "artifact-smoke-model",
"api_key_env": "THREADKEEPER_ARTIFACT_SMOKE_KEY",
"base_url": "http://localhost:11434",
"node_role": "local",
"endpoint_kind": "ollama_native",
"default_tool_subset": ["write-file"],
}, indent=2, sort_keys=True), encoding="utf-8")
return persona_dir


def run_smoke(base: Path) -> dict:
base.mkdir(parents=True, exist_ok=True)
persona_dir = _write_smoke_persona(base)
workspace = base / "workspace"
run_dir = base / "runs"
workspace.mkdir(parents=True, exist_ok=True)
os.environ["THREADKEEPER_ARTIFACT_SMOKE_KEY"] = "dummy-local-smoke-key"
os.environ["OMEGACLAW_SUBAGENT_WORKSPACE"] = str(workspace)
os.environ["OMEGACLAW_SUBAGENT_QUEUE_ONLY"] = "1"
subagent.PERSONA_DIR = str(persona_dir)
subagent.SUBAGENT_RUN_DIR = str(run_dir)
subagent._SUBAGENT_MAX_QUEUED_DISPATCHES = 2

queued = json.loads(subagent.dispatch(
json.dumps({
"objective": "artifact-local queued worker loop smoke",
"allowed_paths": ["smoke.txt"],
"max_tool_calls": 0,
}, sort_keys=True),
"write-file",
"artifact-smoke",
max_turns=1,
max_chars=1200,
))
if queued.get("status") != "queued":
raise RuntimeError(f"expected queued status, got {queued}")

def fake_worker_llm(*_args):
return ('(emit "artifact-local queued worker loop smoke ok")', 3, 5)

subagent._call_subagent_llm = fake_worker_llm
worker = json.loads(subagent.run_queued_worker_loop(
max_tasks=1,
poll_interval_s=0,
max_idle_polls=0,
max_runtime_s=30,
))
return {
"status": "smoke_passed" if worker.get("status") == "worker_drained" else "smoke_failed",
"queued": queued,
"worker": worker,
"workspace": str(workspace),
"run_dir": str(run_dir),
}


def main(argv=None) -> int:
argv = list(argv if argv is not None else sys.argv[1:])
if argv:
base = Path(argv[0]).resolve()
result = run_smoke(base)
else:
with tempfile.TemporaryDirectory(prefix="threadkeeper-worker-loop-smoke-") as td:
result = run_smoke(Path(td))
print(json.dumps(result, ensure_ascii=False, sort_keys=True, indent=2))
return 0 if result["status"] == "smoke_passed" else 1


if __name__ == "__main__":
raise SystemExit(main())
Loading