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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,16 @@ Higher-risk actions stay queued for analyst approval and application:
- threshold tuning
- escalation closures such as `needs_review` or `tune_policy`

## Guarded Research Autonomy

The Local Codex/OpenCodex bridge can complete repeatable research review work with the selected Kimi, Grok, Gemini, or Codex model. In `agent_review` mode it accepts bounded pipeline proposals, records an evidence-linked verdict, and reruns publication safety automatically:

```bash
secopsai intelligence bridge service install --autonomy-mode agent_review
```

Core guardrails prevent local dependency absence from becoming a benign verdict, constrain verdict evidence to the active pipeline, and reduce conclusions that lack sufficient confidence or proof. Package execution, external sandbox submission, disclosure delivery, customer-control changes, and final publication remain human-approved actions. See [Research Automation](docs/research-automation.md).

## Background Monitoring

Example operational model:
Expand Down
16 changes: 16 additions & 0 deletions docs/implementation-checkpoints.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Implementation Checkpoints

## 108 Guarded Agent Research Completion

Status: implementation complete; full verification and Mission Control release are pending.

Added an optional `agent_review` mode for the Local Codex/OpenCodex bridge. When all bounded static-analysis jobs finish, Core accepts pipeline-scoped proposals, records an evidence-linked verdict, and reruns publication safety without requiring card-by-card operator acceptance. Verdict output is schema constrained and then independently guarded by Core: low-confidence work remains inconclusive, a credible verdict requires advisory-backed or sandbox evidence, unresolved contradictions and unsupported claims reduce certainty, and absence from a local repository can never establish package benignness or make ecosystem intelligence non-actionable.

The model receives normalized evidence only. Verdict evidence is limited to active records created by the same investigation pipeline. Every decision records the actor and pipeline revision, and repeated completion is idempotent. The automation cannot execute packages, submit artifacts to an external sandbox, send disclosure, change customer controls, approve publication, or publish content.

Operator surfaces:

- `secopsai research pipeline agent-complete RPL-...`
- `secopsai intelligence bridge service install --autonomy-mode agent_review`
- Mission Control **Complete Agent Review**

The remaining human gates are external sandbox submission, external disclosure delivery, and final publication approval.

## 107 Hermes Agent v1.0.0 Integration

Status: complete in production.
Expand Down
45 changes: 38 additions & 7 deletions docs/research-automation.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,44 @@ The normal workflow no longer requires an evidence-bundle export, a file upload,
4. Core collects official-registry metadata and the package artifact using bounded static-intake controls. It records hashes, inspects the archive without execution, and performs a deterministic comparison when a trusted reference was supplied.
5. Core queues three durable Intelligence jobs. The installed Local Codex Bridge reads only minimized case and static-analysis context and writes structured proposals back to the same pipeline record.
6. Mission Control refreshes the running pipeline automatically.
7. Review every proposed fact, inference, unsupported claim, contradiction, missing-evidence group, recommended-action group, disclosure draft, and publication-risk group. Related model list items are deduplicated and grouped into one editable review card so the queue remains usable. Click **Accept** or **Reject**.
8. Accepted static evidence is attached with its pipeline and review provenance. Accepted model text becomes an immutable analyst-reviewed case note. Rejected proposals remain auditable and do not change canonical evidence.
9. Record the human verdict, approve any sandbox request, send disclosure, and approve publication through their existing separate gates.
7. In **agent review** mode, SecOpsAI accepts the bounded evidence proposals, records an evidence-linked agent verdict, and reruns publication safety automatically. The verdict is constrained by deterministic guardrails: local absence cannot prove benignness, `credible` requires advisory-backed or sandbox evidence, and low-confidence work remains `inconclusive`.
8. In **supervised** mode, review each proposal manually with **Accept** or **Reject**, then record the verdict yourself.
9. Review the resulting case. External sandbox submission, external disclosure delivery, and final publication approval remain separate human gates.

If the bridge or collection step fails, click **Retry from checkpoint**. A new pipeline revision is created, stale proposals are superseded, and the previous revision remains auditable. If comparison was incomplete, enter a verified reference and click **Add reference and rerun analysis**.

The pipeline cannot classify a package as malicious, execute it, submit it to a sandbox, send external communication, approve publication, or publish an article.
The pipeline never executes package code, submits an artifact to an external sandbox, sends external communication, approves publication, or publishes an article. In agent-review mode it may record a bounded case verdict, but only against accepted, pipeline-specific evidence and with all guardrail decisions retained in the audit trail.

## Agent-review mode

Agent-review mode is the recommended high-automation setting for a local research workstation. It delegates the repeatable evidence review, verdict recommendation, analyst brief, and publication preflight to the selected Local Codex/OpenCodex model. Models remain fallible; SecOpsAI therefore validates their schema, evidence links, confidence, contradictions, and local-exposure reasoning before writing a verdict.

Install or update the background bridge in agent-review mode:

```bash
secopsai intelligence bridge service install --autonomy-mode agent_review
```

Complete an already-waiting pipeline from the CLI:

```bash
secopsai research pipeline agent-complete RPL-XXXXXXXXXXXXXXXX
```

Mission Control exposes the same operation as **Complete Agent Review**. The action:

- accepts bounded static evidence and model proposals;
- records a verdict with confidence, rationale, evidence identifiers, model provider, and pipeline revision;
- blocks benign or not-substantiated conclusions based only on missing local exposure;
- downgrades unsupported or contradictory claims;
- reruns publication safety;
- does not upload an artifact, contact a third party, or publish content.

Use supervised mode when policy requires proposal-by-proposal human acceptance:

```bash
secopsai intelligence bridge service install --autonomy-mode supervised
```

## Granular recovery controls

Expand All @@ -29,7 +60,7 @@ Use the individual actions only when diagnosing a step or deliberately running a
3. Use **Collect Metadata Preview** to confirm the official registry target.
4. Use **Run Safe Package Intake**. The artifact is fetched into local quarantine, hashed, and inspected in memory. No package manager or build command runs.
5. Review the job result and indicators. Use **Attach Verified Evidence** only after reviewing the preview.
6. Use **Generate Evidence Matrix** and record a human verdict with rationale and evidence IDs.
6. Use **Generate Evidence Matrix** and either click **Complete Agent Review** or record a human verdict with rationale and evidence IDs.
7. Use **Run Publication Safety Check** before drafting public content.
8. Use **Prepare Disclosure** to create a reviewable maintainer/registry message. Approval and sending are separate actions.
9. Use **Request Sandbox Approval** only when static evidence leaves an important runtime question. The default provider is manual-result-import; Core never executes packages locally.
Expand All @@ -50,7 +81,7 @@ secopsai research workflow evidence-matrix RSC-XXXXXXXXXXXX
secopsai research workflow publication-check RSC-XXXXXXXXXXXX
```

Human-gated actions are explicit:
Human-gated external actions are explicit:

```bash
secopsai research workflow verdict RSC-XXXXXXXXXXXX --verdict likely --confidence 70 --rationale "Explain the evidence and limitations." --evidence-id EVD-XXXXXXXXXXXX
Expand All @@ -63,4 +94,4 @@ secopsai research workflow publication-approve RSC-XXXXXXXXXXXX --review-id PUB-

Artifacts are bounded by size, redirect, archive-entry, expanded-content, and inspected-text limits. Absolute paths, traversal, links, devices, archive bombs, private-address resolutions, credential-bearing URLs, and unapproved redirects are rejected. Static indicators are evidence leads, not proof of maliciousness. Raw artifacts and raw registry responses are not sent to AI.

Dynamic analysis is not enabled by pretending the Core host is a sandbox. Configure a dedicated isolated provider or import a sanitized result manually. Disclosure sending and publication approval remain auditable human decisions.
Dynamic analysis is not enabled by pretending the Core host is a sandbox. Configure a dedicated isolated provider or import a sanitized result manually. External sandbox submission, disclosure sending, and publication approval remain auditable human decisions. A model may prepare the justification or draft, but it cannot perform the external action.
26 changes: 25 additions & 1 deletion secopsai/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@
set_sandbox_status,
)
from secopsai.research_pipeline import (
agent_complete_pipeline,
get_pipeline as get_research_pipeline,
list_pipelines as list_research_pipelines,
resume_investigation_pipeline,
Expand Down Expand Up @@ -1198,6 +1199,12 @@ def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
intelligence_bridge_service.add_argument("--db-path", default=None)
intelligence_bridge_service.add_argument("--no-start", action="store_true")
intelligence_bridge_service.add_argument("--tail", type=int, default=80)
intelligence_bridge_service.add_argument(
"--autonomy-mode",
choices=["supervised", "agent_review"],
default="supervised",
help="Automatically complete bounded research review when set to agent_review",
)

research = sub.add_parser("research", help="Generate source-backed research reports and preflight checks")
research_sub = research.add_subparsers(dest="research_cmd", required=True)
Expand Down Expand Up @@ -1615,6 +1622,13 @@ def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
pipeline_auto.add_argument("pipeline_id")
pipeline_auto.add_argument("--actor", default="analyst")
pipeline_auto.add_argument("--db-path", default=None)
pipeline_agent = research_pipeline_sub.add_parser(
"agent-complete",
help="Accept bounded proposals and record an evidence-linked agent verdict without external actions",
)
pipeline_agent.add_argument("pipeline_id")
pipeline_agent.add_argument("--actor", default="secopsai-agent-autonomy")
pipeline_agent.add_argument("--db-path", default=None)

workflow = research_sub.add_parser("workflow", help="Evidence, verdict, disclosure, publication, and sandbox gates")
workflow_sub = workflow.add_subparsers(dest="research_workflow_cmd", required=True)
Expand Down Expand Up @@ -2354,6 +2368,12 @@ def _run_research_automation_command(args: argparse.Namespace) -> int:
actor=args.actor,
db_path=args.db_path,
)
elif args.research_pipeline_cmd == "agent-complete":
payload = agent_complete_pipeline(
args.pipeline_id,
actor=args.actor,
db_path=args.db_path,
)
else:
payload = review_pipeline_item(
args.pipeline_id,
Expand Down Expand Up @@ -3234,7 +3254,11 @@ def main(argv: Optional[List[str]] = None) -> int:
)
elif args.intelligence_bridge_cmd == "service":
if args.action == "install":
payload = install_codex_bridge_service(db_path=args.db_path, start=not args.no_start)
payload = install_codex_bridge_service(
db_path=args.db_path,
start=not args.no_start,
autonomy_mode=args.autonomy_mode,
)
else:
payload = codex_bridge_service_action(args.action, tail=args.tail)
else:
Expand Down
12 changes: 12 additions & 0 deletions secopsai/codex_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,7 @@ def _normalize_bridge_result(payload: dict[str, Any]) -> dict[str, Any]:
"evidence",
"recommended_actions",
"limitations",
"verdict_evidence_refs",
)
for field_name in list_fields:
values = result.get(field_name)
Expand Down Expand Up @@ -747,6 +748,17 @@ def _normalize_bridge_result(payload: dict[str, Any]) -> dict[str, Any]:
if not _present(result.get("limitations")):
missing = result.get("missing_evidence")
result["limitations"] = list(missing[:25]) if isinstance(missing, list) else []
recommendation = str(result.get("verdict_recommendation") or "").strip().lower()
if recommendation not in {"credible", "likely", "inconclusive", "not_substantiated", "benign"}:
result["verdict_recommendation"] = "inconclusive"
try:
result["verdict_confidence"] = max(0, min(int(result.get("verdict_confidence") or 0), 100))
except (TypeError, ValueError):
result["verdict_confidence"] = 0
if not _present(result.get("verdict_rationale")):
result["verdict_rationale"] = "The model did not provide a bounded package verdict rationale."
if not isinstance(result.get("verdict_evidence_refs"), list):
result["verdict_evidence_refs"] = []
return result


Expand Down
16 changes: 12 additions & 4 deletions secopsai/codex_bridge_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,18 @@ def install_service(
home: Path | None = None,
platform_name: str | None = None,
runner: RunCommand | None = None,
autonomy_mode: str = "supervised",
) -> dict[str, Any]:
resolved_home = (home or Path.home()).expanduser().resolve()
system = (platform_name or platform.system()).lower()
run = runner or _run
autonomy_mode = str(autonomy_mode or "supervised").strip().lower()
if autonomy_mode not in {"supervised", "agent_review"}:
raise ValueError("autonomy mode must be supervised or agent_review")
if system == "darwin":
result = _install_launchd(resolved_home, db_path, run, start)
result = _install_launchd(resolved_home, db_path, run, start, autonomy_mode)
elif system == "linux":
result = _install_systemd(resolved_home, db_path, run, start)
result = _install_systemd(resolved_home, db_path, run, start, autonomy_mode)
else:
raise ValueError("automatic Codex bridge service installation supports macOS and Linux")
return {"status": "installed", "service": LABEL, **result}
Expand Down Expand Up @@ -60,7 +64,7 @@ def service_action(
raise ValueError("Codex bridge service controls support macOS and Linux")


def _install_launchd(home: Path, db_path: str | None, run: RunCommand, start: bool) -> dict[str, Any]:
def _install_launchd(home: Path, db_path: str | None, run: RunCommand, start: bool, autonomy_mode: str) -> dict[str, Any]:
launch_agents = home / "Library" / "LaunchAgents"
logs = home / "Library" / "Logs" / "SecOpsAI"
launch_agents.mkdir(parents=True, exist_ok=True)
Expand All @@ -80,6 +84,7 @@ def _install_launchd(home: Path, db_path: str | None, run: RunCommand, start: bo
environment = {
"HOME": str(home),
"PATH": os.environ.get("PATH", "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"),
"SECOPSAI_RESEARCH_AUTONOMY_MODE": autonomy_mode,
}
if os.environ.get("CODEX_HOME"):
environment["CODEX_HOME"] = os.environ["CODEX_HOME"]
Expand Down Expand Up @@ -109,10 +114,11 @@ def _install_launchd(home: Path, db_path: str | None, run: RunCommand, start: bo
"started": start,
"logs": [str(logs / "codex-bridge.out.log"), str(logs / "codex-bridge.err.log")],
"credentials_persisted": False,
"autonomy_mode": autonomy_mode,
}


def _install_systemd(home: Path, db_path: str | None, run: RunCommand, start: bool) -> dict[str, Any]:
def _install_systemd(home: Path, db_path: str | None, run: RunCommand, start: bool, autonomy_mode: str) -> dict[str, Any]:
unit_dir = home / ".config" / "systemd" / "user"
logs = home / ".local" / "state" / "secopsai"
unit_dir.mkdir(parents=True, exist_ok=True)
Expand All @@ -138,6 +144,7 @@ def _install_systemd(home: Path, db_path: str | None, run: RunCommand, start: bo
"Type=simple",
f"WorkingDirectory={working_directory}",
f"ExecStart={shlex.join(command)}",
f"Environment=SECOPSAI_RESEARCH_AUTONOMY_MODE={autonomy_mode}",
"Restart=on-failure",
"RestartSec=15",
"NoNewPrivileges=true",
Expand All @@ -160,6 +167,7 @@ def _install_systemd(home: Path, db_path: str | None, run: RunCommand, start: bo
"started": start,
"logs": [f"journalctl --user -u {SYSTEMD_UNIT}"],
"credentials_persisted": False,
"autonomy_mode": autonomy_mode,
}


Expand Down
22 changes: 21 additions & 1 deletion secopsai/intelligence.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ def _bridge_instructions(action: Action) -> str:
"analyze_research_case": (
"Also return confirmed_facts, inferences, unsupported_claims, contradictions, and missing_evidence as arrays. "
"A confirmed fact must cite supplied normalized evidence; otherwise classify it as an inference or unsupported claim. "
"Return verdict_recommendation as credible, likely, inconclusive, not_substantiated, or benign; "
"verdict_confidence as 0-100; verdict_rationale; and verdict_evidence_refs using only supplied evidence or pipeline step identifiers. "
"Never downgrade a package merely because local exposure was not observed. "
),
"generate_analyst_brief": (
"Also return an article_outline array. Keep it suitable for a technical draft, not publication-ready copy. "
Expand All @@ -280,7 +283,9 @@ def _bridge_instructions(action: Action) -> str:
f"Perform the approved SecOpsAI action '{action.name}'. Use only the supplied normalized context. "
"Do not claim that missing evidence was observed. Distinguish facts, inferences, and limitations. "
"Do not execute commands, access files, browse, contact external parties, change product state, or approve publication. "
f"{action_guidance}Return concise JSON matching the required output schema. Human review is mandatory."
f"{action_guidance}For actions other than analyze_research_case, set verdict_recommendation to inconclusive, "
"verdict_confidence to 0, verdict_rationale to 'Verdict not assessed by this action', and verdict_evidence_refs to an empty array. "
"Return concise JSON matching the required output schema. Model output is evidence-bounded and subject to SecOpsAI guardrails."
)


Expand All @@ -299,6 +304,10 @@ def bridge_output_schema() -> dict[str, Any]:
"publication_risks",
"article_outline",
"disclosure_draft",
"verdict_recommendation",
"verdict_confidence",
"verdict_rationale",
"verdict_evidence_refs",
]
return {
"$schema": "https://json-schema.org/draft/2020-12/schema",
Expand All @@ -319,6 +328,17 @@ def bridge_output_schema() -> dict[str, Any]:
"publication_risks": {"type": "array", "maxItems": 50, "items": {"type": "string", "maxLength": 2000}},
"article_outline": {"type": "array", "maxItems": 30, "items": {"type": "string", "maxLength": 2000}},
"disclosure_draft": {"type": "string", "maxLength": 12000},
"verdict_recommendation": {
"type": "string",
"enum": ["credible", "likely", "inconclusive", "not_substantiated", "benign"],
},
"verdict_confidence": {"type": "integer", "minimum": 0, "maximum": 100},
"verdict_rationale": {"type": "string", "maxLength": 8000},
"verdict_evidence_refs": {
"type": "array",
"maxItems": 50,
"items": {"type": "string", "maxLength": 200},
},
},
}

Expand Down
Loading
Loading