Skip to content
Merged
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
1 change: 1 addition & 0 deletions .github/workflows/images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ jobs:
- { image: evidence-console, context: apps/evidence-console, dockerfile: Dockerfile }
- { image: evidence-receipts, context: apps/evidence-receipts, dockerfile: Dockerfile }
- { image: eval-fabric-api, context: apps/eval-fabric-api, dockerfile: Dockerfile }
- { image: agentic-os-api, context: apps/agentic-os-api, dockerfile: Dockerfile }
- { image: hellgraph-service, context: apps/hellgraph-service, dockerfile: Dockerfile }
- { image: osm-map-api, context: ., dockerfile: apps/osm-map-api/Dockerfile }
- { image: workspace-mail, context: services/workspace-mail, dockerfile: Dockerfile }
Expand Down
7 changes: 7 additions & 0 deletions apps/agentic-os-api/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt ./requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
COPY app ./app
EXPOSE 8080
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"]
25 changes: 25 additions & 0 deletions apps/agentic-os-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# agentic-os-api

The coordination service for the **agentic operating system** — agent pods
pursuing objectives across the estate under a governed capture cadence.

Serves the canonical agentic-OS objects (`Opportunity` / `AgentPod` /
`ReadinessScore` / `CaptureCadence`) that the cockpit console renders and the
pods coordinate against. Object shapes conform to the **sourceos-spec**
agentic-OS contract and compose over **prophet-workspace** (ProfessionalWorkroom
/ OrgGovControlRoom) and **prophet-mesh** (agent-choir + estate graph).

Read-only seed today; a live registry adapter will resolve the same URNs from the
workspace + estate graph.

## Endpoints
- `GET /health`
- `GET /opportunities` · `GET /opportunities/{slug}` (with readiness + cadence)
- `GET /pods`
- `GET /cadence`

## Run
```
pip install -r requirements.txt
uvicorn app.main:app --port 8080
```
Empty file.
86 changes: 86 additions & 0 deletions apps/agentic-os-api/app/data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""Canonical agentic-OS objects served by the coordination service.

Shapes conform to the sourceos-spec agentic-OS contract (Opportunity / AgentPod /
ReadinessScore / CaptureCadence), which composes over prophet-workspace
(ProfessionalWorkroom / OrgGovControlRoom) and prophet-mesh (agent-choir + estate
graph). This is the seed dataset the service serves until a live registry adapter
resolves the same URNs from the workspace + estate graph.
"""
from __future__ import annotations

READINESS_DIMS = [
"buyerProblem", "solutionHypothesis", "sharedLibraries", "agentPod", "partnerArchetype",
"namedPartnerTargets", "oemLane", "artifactPack", "deltaControl", "questions", "pricing", "pastPerformance",
]

PODS = [
{"id": "urn:srcos:agent-pod:capture-lead", "type": "AgentPod", "specVersion": "2.0.0",
"role": "Capture Lead", "mandate": "Own pursuit strategy, buyer map, milestones, and decisions.",
"inputs": ["signals", "updates", "Q&A", "competitive intel"],
"outputs": ["pursuit plan", "gate decisions", "action backlog"],
"repoAnchors": ["sociosphere", "socioprophet"], "status": "active", "choirRole": "governance-sentinel"},
{"id": "urn:srcos:agent-pod:technical-solution", "type": "AgentPod", "specVersion": "2.0.0",
"role": "Technical Solution", "mandate": "Design service model, architecture, transition, operating model.",
"inputs": ["scope", "priorities", "reusable patterns"], "outputs": ["solution narrative", "architecture"],
"repoAnchors": ["prophet-platform", "prophet-platform-standards"], "status": "active", "choirRole": "planning-agent"},
{"id": "urn:srcos:agent-pod:evidence-qa", "type": "AgentPod", "specVersion": "2.0.0",
"role": "Evidence / QA", "mandate": "Run review gates and coherence checks across all objectives.",
"inputs": ["draft artifacts", "matrices"], "outputs": ["gate report", "defect list"],
"repoAnchors": ["agentplane", "policy-fabric"], "status": "active", "choirRole": "governance-sentinel"},
]

CADENCE = {
"id": "urn:srcos:capture-cadence:standard-8wk", "type": "CaptureCadence", "specVersion": "2.0.0",
"name": "Standard 8-week capture sprint", "currentWeek": 4,
"weeks": [
{"week": 0, "objective": "Intake + normalize", "minReadiness": 0.2, "exitDecision": "pursue / watch / pause"},
{"week": 4, "objective": "Artifact pack v1", "minReadiness": 0.5, "exitDecision": "Gate 1 complete"},
{"week": 8, "objective": "Delta sprint", "minReadiness": 0.8, "exitDecision": "go-forward after delta"},
],
}


def _readiness(opp_slug: str, scores: dict[str, int]) -> dict:
full = {d: scores.get(d, 0) for d in READINESS_DIMS}
total = sum(full.values())
pct = round(total / (len(READINESS_DIMS) * 3) * 100)
rag = "Green" if pct >= 70 else "Amber" if pct >= 40 else "Red"
return {
"id": f"urn:srcos:readiness-score:{opp_slug}", "type": "ReadinessScore", "specVersion": "2.0.0",
"opportunityRef": f"urn:srcos:opportunity:{opp_slug}", "dimensions": full,
"total": total, "max": 36, "readinessPct": pct, "rag": rag, "nextGate": "Gate 1",
"policyRef": "policy://capture/burden-of-proof/gate-1",
}


OPPORTUNITIES = [
{"id": "urn:srcos:opportunity:health-devsecops", "type": "Opportunity", "specVersion": "2.0.0",
"name": "Health Services DevSecOps", "cluster": "Health", "missionOwner": "PDS",
"buyingProblem": "Sustain legacy health apps while modernizing safely outside the Oracle Health baseline.",
"deliveryPattern": "Continuity-plus-modernization delivery cell; governed DevSecOps.",
"reuseRepos": ["prophet-platform", "agentplane", "policy-fabric"],
"podRefs": ["urn:srcos:agent-pod:capture-lead", "urn:srcos:agent-pod:technical-solution", "urn:srcos:agent-pod:evidence-qa"],
"workroomRef": "workroom://health-devsecops", "controlRoomRef": "controlroom://health-devsecops",
"telos": {"objective": "Intelligence serves human flourishing", "constraints": ["non-domination", "consent", "dignity"]},
"readinessRef": "urn:srcos:readiness-score:health-devsecops", "status": "Active",
"winTheme": "We preserve continuity while industrializing governed modernization."},
{"id": "urn:srcos:opportunity:zta-acceleration", "type": "Opportunity", "specVersion": "2.0.0",
"name": "ZTA Acceleration", "cluster": "Cyber", "missionOwner": "OIS",
"buyingProblem": "Accelerate zero-trust across identity, network, and data without breaking operations.",
"deliveryPattern": "Identity-control-plane cell with runtime identity semantics and evidence refs.",
"reuseRepos": ["mcp-a2a-zero-trust", "policy-fabric"],
"podRefs": ["urn:srcos:agent-pod:technical-solution", "urn:srcos:agent-pod:evidence-qa"],
"workroomRef": "workroom://zta-acceleration", "controlRoomRef": "controlroom://zta-acceleration",
"telos": {"objective": "Intelligence serves human flourishing", "constraints": ["non-domination", "consent", "dignity"]},
"readinessRef": "urn:srcos:readiness-score:zta-acceleration", "status": "Active",
"winTheme": "Zero trust as a governed control plane with evidence at every grant."},
]

READINESS = {
"health-devsecops": _readiness("health-devsecops", {"buyerProblem": 3, "solutionHypothesis": 2, "sharedLibraries": 3, "agentPod": 2, "partnerArchetype": 2, "oemLane": 1, "artifactPack": 2, "deltaControl": 2, "questions": 1}),
"zta-acceleration": _readiness("zta-acceleration", {"buyerProblem": 3, "solutionHypothesis": 2, "sharedLibraries": 3, "agentPod": 2, "oemLane": 1, "artifactPack": 2, "deltaControl": 2}),
}


def opp_slug(opp: dict) -> str:
return opp["id"].rsplit(":", 1)[-1]
44 changes: 44 additions & 0 deletions apps/agentic-os-api/app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Agentic OS API — the coordination service for the agentic operating system.

Serves the canonical agentic-OS objects (Opportunity / AgentPod / ReadinessScore
/ CaptureCadence) the cockpit console renders and the pods coordinate against.
Objects conform to the sourceos-spec agentic-OS contract and compose over
prophet-workspace (workrooms) + prophet-mesh (choir + estate graph). Read-only
seed for now; a live registry adapter resolves the same URNs from the workspace
and estate graph.
"""
from __future__ import annotations

from fastapi import FastAPI, HTTPException

from .data import CADENCE, OPPORTUNITIES, PODS, READINESS, opp_slug

app = FastAPI(title="Prophet Platform Agentic OS API", version="0.1.0")


@app.get("/health")
def health() -> dict:
return {"status": "ok", "service": "agentic-os-api", "objectives": len(OPPORTUNITIES), "pods": len(PODS)}


@app.get("/opportunities")
def list_opportunities() -> dict:
return {"opportunities": OPPORTUNITIES, "count": len(OPPORTUNITIES)}


@app.get("/opportunities/{slug}")
def get_opportunity(slug: str) -> dict:
opp = next((o for o in OPPORTUNITIES if opp_slug(o) == slug), None)
if opp is None:
raise HTTPException(status_code=404, detail=f"opportunity {slug!r} not found")
return {"opportunity": opp, "readiness": READINESS.get(slug), "cadence": CADENCE}


@app.get("/pods")
def list_pods() -> dict:
return {"pods": PODS, "count": len(PODS)}


@app.get("/cadence")
def get_cadence() -> dict:
return CADENCE
2 changes: 2 additions & 0 deletions apps/agentic-os-api/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
testpaths = tests
2 changes: 2 additions & 0 deletions apps/agentic-os-api/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pytest>=8.0
httpx>=0.27
2 changes: 2 additions & 0 deletions apps/agentic-os-api/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fastapi==0.115.0
uvicorn==0.30.6
47 changes: 47 additions & 0 deletions apps/agentic-os-api/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Tests for the Agentic OS coordination API."""
from fastapi.testclient import TestClient

from app.data import READINESS_DIMS
from app.main import app

client = TestClient(app)


def test_health():
r = client.get("/health")
assert r.status_code == 200
assert r.json()["status"] == "ok"


def test_opportunities_conform_to_contract():
r = client.get("/opportunities")
assert r.status_code == 200
opps = r.json()["opportunities"]
assert len(opps) >= 2
for o in opps:
assert o["type"] == "Opportunity"
assert o["id"].startswith("urn:srcos:opportunity:")
# Composes over workspace + carries the Telos.
assert o["workroomRef"].startswith("workroom://")
assert set(o["telos"]) == {"objective", "constraints"}


def test_opportunity_detail_has_readiness_and_cadence():
r = client.get("/opportunities/health-devsecops")
assert r.status_code == 200
body = r.json()
rd = body["readiness"]
assert rd["type"] == "ReadinessScore"
assert set(rd["dimensions"]) == set(READINESS_DIMS)
assert rd["total"] == 18 and rd["readinessPct"] == 50 and rd["rag"] == "Amber"
assert body["cadence"]["currentWeek"] == 4


def test_pods_align_to_choir():
pods = client.get("/pods").json()["pods"]
assert all(p["id"].startswith("urn:srcos:agent-pod:") for p in pods)
assert any(p["choirRole"] == "governance-sentinel" for p in pods)


def test_missing_opportunity_404():
assert client.get("/opportunities/nope").status_code == 404
1 change: 1 addition & 0 deletions deploy/argocd/platform-services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ spec:
- { name: evidence-console }
- { name: evidence-receipts }
- { name: eval-fabric-api }
- { name: agentic-os-api }
- { name: hellgraph-service }
- { name: osm-map-api }
- { name: reasoning-failure-runner }
Expand Down
3 changes: 3 additions & 0 deletions deploy/values/agentic-os-api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# agentic-os-api
image: { repository: agentic-os-api, tag: latest }
service: { port: 8080, portName: http }
Loading