Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ turtle-smoke:
bash -n packaging/scripts/install-turtle-term.sh
bash -n packaging/scripts/package-turtle-term.sh
bash -n packaging/scripts/bootstrap-homebrew-tap.sh
python3 -m py_compile packaging/scripts/render-stable-homebrew-formula.py assets/sourceos/bin/sourceos-term assets/sourceos/bin/turtle-term
python3 -m py_compile packaging/scripts/render-stable-homebrew-formula.py assets/sourceos/bin/sourceos-term assets/sourceos/bin/turtle-term assets/sourceos/bin/turtle-agent-status

turtle-package: turtle-build turtle-smoke
bash packaging/scripts/package-turtle-term.sh "$${TURTLE_TERM_VERSION:-turtle-term-dev}" "$${TURTLE_TERM_TARGET:-$$(uname -s)-$$(uname -m)}"
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,45 @@ turtleterm
turtle-term paths
turtle-term run -- echo hello
turtle-agentctl --stdio ping
turtle-agent-status --json
turtle-tmux panes
```

`turtle-term` is the command wrapper. `turtleterm` is the graphical launcher. `sourceos-term` remains available for SourceOS contract compatibility.

## Agent reliability status

`turtle-agent-status` is a read-only local operator view for SourceOS Agent Reliability evidence. It scans a workspace for local `.sourceos` artifacts and reports whether the agent lane is `ready`, `blocked`, `needs_review`, or has `no_artifacts`.

It reads:

- `.sourceos/logs/guardrail-decisions.jsonl`
- `StopGateArtifact` files named `stop-gate-artifact.json`
- `GuardedInvocationArtifact` files named `guarded-invocation-artifact.json`
- SocioSphere `AgentReliabilityGovernanceQueue` files matching `*governance-queue*.json`

Human-readable mode:

```bash
turtle-agent-status --root .
```

JSON mode:

```bash
turtle-agent-status --root . --json
```

The command is read-only. It does not modify policy, memory, git state, agent state, or governance queues.

## Product surfaces

- TurtleTerm graphical launcher
- TurtleTerm mux launcher
- TurtleTerm command wrapper
- TurtleTerm local agent gateway
- TurtleTerm agent CLI
- TurtleTerm agent reliability status CLI
- TurtleTerm tmux bridge
- TurtleTerm skill manifests
- TurtleTerm turtle icon
Expand Down
12 changes: 7 additions & 5 deletions assets/sourceos/bin/turtle-agent-machine
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,27 @@ set -eu

bin_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
agentctl="$bin_dir/turtle-agentctl"
if [ ! -x "$agentctl" ]; then
agentctl="turtle-agentctl"
if [ -f "$agentctl" ]; then
agentctl_prefix="python3 $agentctl"
else
agentctl_prefix="turtle-agentctl"
fi

cmd="${1:-surfaces}"
case "$cmd" in
surfaces)
shift || true
exec "$agentctl" --stdio agent-machine-surfaces "$@"
exec $agentctl_prefix --stdio agent-machine-surfaces "$@"
;;
probe)
shift || true
exec "$agentctl" --stdio agent-machine-probe "$@"
exec $agentctl_prefix --stdio agent-machine-probe "$@"
;;
request-execution)
shift || true
surface="${1:-agent-machine/local-agentpod}"
shift || true
exec "$agentctl" --stdio request-surface-execution "$surface" "$@"
exec $agentctl_prefix --stdio request-surface-execution "$surface" "$@"
;;
*)
echo "usage: turtle-agent-machine [surfaces|probe|request-execution <surface> -- <command>]" >&2
Expand Down
207 changes: 207 additions & 0 deletions assets/sourceos/bin/turtle-agent-status
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
#!/usr/bin/env python3
"""TurtleTerm local SourceOS Agent Reliability status view.

Reads local SourceOS evidence artifacts and summarizes:
- blocking guardrail decisions;
- stop-gate outcomes;
- guarded invocation outcomes;
- pending governance queue items.

This tool is read-only. It does not modify policy, memory, git state, or queue
artifacts.
"""

from __future__ import annotations

import argparse
import json
from collections import Counter
from pathlib import Path
from typing import Any

BLOCKING_DECISIONS = {"deny", "quarantine", "defer", "escalate"}
NEEDS_REVIEW_QUEUE_STATUSES = {"pending"}


def load_json(path: Path) -> dict[str, Any] | None:
try:
data = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return None
return data if isinstance(data, dict) else None


def load_jsonl(path: Path) -> list[dict[str, Any]]:
if not path.exists():
return []
rows: list[dict[str, Any]] = []
try:
for line in path.read_text(encoding="utf-8").splitlines():
if not line.strip():
continue
try:
data = json.loads(line)
except json.JSONDecodeError:
rows.append({"schema": "invalid-json", "decision": "invalid", "decisionId": f"{path}:invalid-json"})
continue
if isinstance(data, dict):
rows.append(data)
except OSError:
return []
return rows


def unique_paths(paths: list[Path]) -> list[Path]:
seen: set[str] = set()
out: list[Path] = []
for path in paths:
key = str(path.resolve())
if key not in seen:
seen.add(key)
out.append(path)
return out


def discover_json_artifacts(root: Path, filenames: set[str]) -> list[dict[str, Any]]:
artifacts: list[dict[str, Any]] = []
paths: list[Path] = []
sourceos = root / ".sourceos"
if sourceos.exists():
for name in filenames:
paths.extend(sourceos.rglob(name))
for path in unique_paths(paths):
data = load_json(path)
if data is not None:
data["_path"] = str(path)
artifacts.append(data)
return artifacts


def discover_governance_queues(root: Path) -> list[dict[str, Any]]:
queues: list[dict[str, Any]] = []
candidates: list[Path] = []
for base in [root / ".sourceos", root / "standards" / "agent-reliability"]:
if base.exists():
candidates.extend(base.rglob("*governance-queue*.json"))
for path in unique_paths(candidates):
data = load_json(path)
if data and data.get("kind") == "AgentReliabilityGovernanceQueue":
data["_path"] = str(path)
queues.append(data)
return queues


def summarize(root: Path) -> dict[str, Any]:
decision_log = root / ".sourceos" / "logs" / "guardrail-decisions.jsonl"
decisions = load_jsonl(decision_log)
decision_counts = Counter(str(item.get("decision", "unknown")) for item in decisions)
blocking = [item for item in decisions if str(item.get("decision", "")).lower() in BLOCKING_DECISIONS]
redactions = [item for item in decisions if str(item.get("decision", "")).lower() == "redact"]

stop_gates = discover_json_artifacts(root, {"stop-gate-artifact.json"})
stop_gate_counts = Counter(str(item.get("result", "unknown")) for item in stop_gates if item.get("kind") == "StopGateArtifact")
failing_stop_gates = [item for item in stop_gates if item.get("kind") == "StopGateArtifact" and item.get("result") in {"fail", "needs_human"}]

invocations = discover_json_artifacts(root, {"guarded-invocation-artifact.json"})
invocation_counts = Counter(str(item.get("result", "unknown")) for item in invocations if item.get("kind") == "GuardedInvocationArtifact")
failed_invocations = [item for item in invocations if item.get("kind") == "GuardedInvocationArtifact" and item.get("result") in {"failure", "blocked", "needs_human"}]

queues = discover_governance_queues(root)
pending_queue_items: list[dict[str, Any]] = []
for queue in queues:
for item in queue.get("items", []):
if isinstance(item, dict) and item.get("status") in NEEDS_REVIEW_QUEUE_STATUSES:
pending = dict(item)
pending["queuePath"] = queue.get("_path")
pending_queue_items.append(pending)

status = "ready"
if blocking or failing_stop_gates or failed_invocations:
status = "blocked"
elif pending_queue_items:
status = "needs_review"
elif not decisions and not stop_gates and not invocations and not queues:
status = "no_artifacts"

return {
"schema": "sourceos.turtle.agent_status.v0",
"root": str(root),
"status": status,
"guardrail": {
"decisionLog": str(decision_log),
"total": len(decisions),
"counts": dict(decision_counts),
"blocking": [item.get("decisionId") or item.get("policyId") for item in blocking],
"redactions": [item.get("decisionId") or item.get("policyId") for item in redactions],
},
"stopGates": {
"total": len(stop_gates),
"counts": dict(stop_gate_counts),
"blocking": [item.get("gateId") or item.get("_path") for item in failing_stop_gates],
},
"invocations": {
"total": len(invocations),
"counts": dict(invocation_counts),
"blocking": [item.get("workcellArtifactRef") or item.get("_path") for item in failed_invocations],
},
"governance": {
"queues": len(queues),
"pending": [
{
"itemId": item.get("itemId"),
"itemType": item.get("itemType"),
"priority": item.get("priority"),
"title": item.get("title"),
"queuePath": item.get("queuePath"),
}
for item in pending_queue_items
],
},
}


def print_human(summary: dict[str, Any]) -> None:
print(f"TurtleTerm Agent Status: {summary['status']}")
print(f"root: {summary['root']}")
print("")
print("Guardrails")
print(f" decisions: {summary['guardrail']['total']} {summary['guardrail']['counts']}")
if summary["guardrail"]["blocking"]:
print(f" blocking: {', '.join(str(x) for x in summary['guardrail']['blocking'])}")
if summary["guardrail"]["redactions"]:
print(f" redactions: {', '.join(str(x) for x in summary['guardrail']['redactions'])}")
print("Stop gates")
print(f" artifacts: {summary['stopGates']['total']} {summary['stopGates']['counts']}")
if summary["stopGates"]["blocking"]:
print(f" blocking: {', '.join(str(x) for x in summary['stopGates']['blocking'])}")
print("Invocations")
print(f" artifacts: {summary['invocations']['total']} {summary['invocations']['counts']}")
if summary["invocations"]["blocking"]:
print(f" blocking: {', '.join(str(x) for x in summary['invocations']['blocking'])}")
print("Governance")
print(f" queues: {summary['governance']['queues']}")
print(f" pending review items: {len(summary['governance']['pending'])}")
for item in summary["governance"]["pending"]:
print(f" - [{item.get('priority')}] {item.get('itemType')}: {item.get('title')} ({item.get('itemId')})")


def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Read local SourceOS agent reliability artifacts and print TurtleTerm status.")
parser.add_argument("--root", default=".", help="Workspace/repo root to inspect")
parser.add_argument("--json", action="store_true", help="Emit JSON instead of human-readable text")
return parser


def main(argv: list[str] | None = None) -> int:
args = build_parser().parse_args(argv)
root = Path(args.root).resolve()
summary = summarize(root)
if args.json:
print(json.dumps(summary, indent=2, sort_keys=True))
else:
print_human(summary)
return 0 if summary["status"] in {"ready", "no_artifacts"} else 2


if __name__ == "__main__":
raise SystemExit(main())
12 changes: 7 additions & 5 deletions assets/sourceos/bin/turtle-cloudfog
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,27 @@ set -eu

bin_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
agentctl="$bin_dir/turtle-agentctl"
if [ ! -x "$agentctl" ]; then
agentctl="turtle-agentctl"
if [ -f "$agentctl" ]; then
agentctl_prefix="python3 $agentctl"
else
agentctl_prefix="turtle-agentctl"
fi

cmd="${1:-surfaces}"
case "$cmd" in
surfaces)
shift || true
exec "$agentctl" --stdio cloudfog-surfaces "$@"
exec $agentctl_prefix --stdio cloudfog-surfaces "$@"
;;
inspect)
shift || true
exec "$agentctl" --stdio cloudfog-inspect "$@"
exec $agentctl_prefix --stdio cloudfog-inspect "$@"
;;
request-execution)
shift || true
surface="${1:-cloudfog/local-devshell}"
shift || true
exec "$agentctl" --stdio request-surface-execution "$surface" "$@"
exec $agentctl_prefix --stdio request-surface-execution "$surface" "$@"
;;
*)
echo "usage: turtle-cloudfog [surfaces|inspect <surface>|request-execution <surface> -- <command>]" >&2
Expand Down
10 changes: 6 additions & 4 deletions assets/sourceos/bin/turtle-superconscious
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@ set -eu

bin_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
agentctl="$bin_dir/turtle-agentctl"
if [ ! -x "$agentctl" ]; then
agentctl="turtle-agentctl"
if [ -f "$agentctl" ]; then
agentctl_prefix="python3 $agentctl"
else
agentctl_prefix="turtle-agentctl"
fi

cmd="${1:-observe}"
case "$cmd" in
observe)
shift || true
exec "$agentctl" --stdio superconscious-observe "$@"
exec $agentctl_prefix --stdio superconscious-observe "$@"
;;
propose)
shift || true
exec "$agentctl" --stdio superconscious-propose "$@"
exec $agentctl_prefix --stdio superconscious-propose "$@"
;;
*)
echo "usage: turtle-superconscious [observe <text>|propose <prompt>]" >&2
Expand Down
Loading
Loading