diff --git a/Makefile b/Makefile index bc46e4d..9590c07 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +.PHONY: validate validate-json validate-yaml validate-quadlet validate-render validate-evidence validate-governance validate-policy-fabric validate-agent-registry validate-activation validate-supply-chain validate-release-bundle validate-sourceos-projections validate-package validate-cli validate-formula doctor probe .PHONY: validate validate-json validate-yaml validate-quadlet validate-render validate-evidence validate-governance validate-policy-fabric validate-activation validate-supply-chain validate-release-bundle validate-sourceos-projections validate-package validate-cli validate-formula doctor probe .PHONY: validate validate-json validate-yaml validate-quadlet validate-render validate-evidence validate-governance validate-policy-fabric validate-agent-registry validate-superconscious-runtime-plan validate-activation validate-supply-chain validate-release-bundle validate-sourceos-projections validate-package validate-cli validate-formula validate-runtime-install-receipts doctor probe @@ -23,6 +24,7 @@ DECIDED_AT := 2026-05-04T12:51:00Z PYCLI := PYTHONPATH=src $(PYTHON) -m agent_machine.cli PYMOD := PYTHONPATH=src $(PYTHON) -m +validate: validate-json validate-yaml validate-quadlet validate-render validate-evidence validate-governance validate-policy-fabric validate-agent-registry validate-activation validate-supply-chain validate-release-bundle validate-sourceos-projections validate-package validate-cli validate-formula validate: validate-json validate-yaml validate-quadlet validate-render validate-evidence validate-governance validate-policy-fabric validate-activation validate-supply-chain validate-release-bundle validate-sourceos-projections validate-package validate-cli validate-formula validate: validate-json validate-yaml validate-quadlet validate-render validate-evidence validate-governance validate-policy-fabric validate-agent-registry validate-superconscious-runtime-plan validate-activation validate-supply-chain validate-release-bundle validate-sourceos-projections validate-package validate-cli validate-formula validate-runtime-install-receipts @@ -64,6 +66,7 @@ validate-policy-fabric: validate-agent-registry: $(PYTHON) scripts/validate-agent-registry.py $(PYTHON) scripts/resolve-agent-registry-grant.py $(LOCAL_AGENTPOD) --grant-dir $(GRANT_DIR) --grant-id urn:srcos:agent-machine:agent-registry-grant:active-loopback-activation --requested-agent-identity-ref urn:srcos:agent:local-inference-provider --session-ref urn:srcos:session:local-bootstrap --agent-machine-id urn:srcos:agent-machine:m2-asahi-local --pretty >/tmp/agent-machine-registry-resolve-active.json + $(PYCLI) registry resolve $(LOCAL_AGENTPOD) --grant-dir $(GRANT_DIR) --grant-id urn:srcos:agent-machine:agent-registry-grant:active-render-only --requested-agent-identity-ref urn:srcos:agent:local-inference-provider --session-ref urn:srcos:session:local-bootstrap --agent-machine-id urn:srcos:agent-machine:m2-asahi-local --pretty >/tmp/agent-machine-pycli-registry-resolve-render-only.json $(PYCLI) registry resolve $(LOCAL_AGENTPOD) --grant-dir $(GRANT_DIR) --grant-id urn:srcos:agent-machine:agent-registry-grant:active-loopback-activation --session-ref urn:srcos:session:local-bootstrap --agent-machine-id urn:srcos:agent-machine:m2-asahi-local --pretty >/tmp/agent-machine-pycli-registry-resolve-active.json validate-superconscious-runtime-plan: @@ -77,6 +80,7 @@ validate-activation: $(PYCLI) activate evaluate $(LOCAL_AGENTPOD) $(FAIL_POLICY) $(FAIL_GRANT) --deployment-receipt-id $(DEPLOYMENT_RECEIPT_ID) --storage-receipt-dir $(RECEIPT_DIR) --decided-at $(DECIDED_AT) --decision-id urn:srcos:agent-machine:activation-decision:local-llama-cpp-fail-closed --pretty >/tmp/agent-machine-pycli-evaluate-activation-fail-closed.json $(BOOTSTRAP_CLI) activate evaluate $(LOCAL_AGENTPOD) $(READY_POLICY) $(READY_GRANT) --deployment-receipt-id $(DEPLOYMENT_RECEIPT_ID) --storage-receipt-dir $(RECEIPT_DIR) --decided-at $(DECIDED_AT) --decision-id urn:srcos:agent-machine:activation-decision:local-llama-cpp-allowed --pretty >/tmp/agent-machine-bootstrap-evaluate-activation-allowed.json $(PYCLI) activate evaluate $(LOCAL_AGENTPOD) $(READY_GRANT) --policy-dir $(POLICY_DIR) --expected-status allowed --deployment-receipt-id $(DEPLOYMENT_RECEIPT_ID) --agent-machine-id urn:srcos:agent-machine:m2-asahi-local --provider-id urn:srcos:agent-machine:inference-provider:asahi-llama-cpp --storage-receipt-dir $(RECEIPT_DIR) --decided-at $(DECIDED_AT) --decision-id urn:srcos:agent-machine:activation-decision:local-llama-cpp-allowed --pretty >/tmp/agent-machine-pycli-resolved-policy-activation-allowed.json + $(PYCLI) activate evaluate $(LOCAL_AGENTPOD) $(READY_POLICY) --grant-dir $(GRANT_DIR) --grant-id urn:srcos:agent-machine:agent-registry-grant:active-loopback-activation --deployment-receipt-id $(DEPLOYMENT_RECEIPT_ID) --requested-agent-identity-ref urn:srcos:agent:local-inference-provider --session-ref urn:srcos:session:local-bootstrap --agent-machine-id urn:srcos:agent-machine:m2-asahi-local --provider-id urn:srcos:agent-machine:inference-provider:asahi-llama-cpp --storage-receipt-dir $(RECEIPT_DIR) --decided-at $(DECIDED_AT) --decision-id urn:srcos:agent-machine:activation-decision:local-llama-cpp-allowed --pretty >/tmp/agent-machine-pycli-resolved-grant-activation-allowed.json $(PYCLI) activate evaluate $(LOCAL_AGENTPOD) --policy-dir $(POLICY_DIR) --expected-status allowed --grant-dir $(GRANT_DIR) --grant-id urn:srcos:agent-machine:agent-registry-grant:active-loopback-activation --session-ref urn:srcos:session:local-bootstrap --deployment-receipt-id $(DEPLOYMENT_RECEIPT_ID) --agent-machine-id urn:srcos:agent-machine:m2-asahi-local --provider-id urn:srcos:agent-machine:inference-provider:asahi-llama-cpp --storage-receipt-dir $(RECEIPT_DIR) --decided-at $(DECIDED_AT) --decision-id urn:srcos:agent-machine:activation-decision:local-llama-cpp-allowed --pretty >/tmp/agent-machine-pycli-resolved-policy-grant-activation-allowed.json $(BOOTSTRAP_CLI) activate evaluate $(LOCAL_AGENTPOD) $(READY_POLICY) $(READY_GRANT) --deployment-receipt-id $(DEPLOYMENT_RECEIPT_ID) --storage-receipt-dir $(RECEIPT_DIR) --decided-at $(DECIDED_AT) --decision-id urn:srcos:agent-machine:activation-decision:local-llama-cpp-allowed --pretty >/tmp/agent-machine-bootstrap-evaluate-activation-allowed.json diff --git a/bin/agent-machine b/bin/agent-machine index 9f03341..2932905 100755 --- a/bin/agent-machine +++ b/bin/agent-machine @@ -71,6 +71,7 @@ Usage: agent-machine policy resolve --policy-dir --deployment-receipt-id [--expected-status allowed] agent-machine activate evaluate [policy.json] --deployment-receipt-id [--policy-dir ] [--storage-receipt-dir ] [--pretty] agent-machine registry resolve --grant-dir --requested-agent-identity-ref --session-ref [--expected-status active] + agent-machine activate evaluate [policy.json] [grant.json] --deployment-receipt-id [--policy-dir ] [--grant-dir ] [--storage-receipt-dir ] [--pretty] agent-machine activate evaluate [policy.json] --deployment-receipt-id [--policy-dir ] [--storage-receipt-dir ] [--pretty] agent-machine steer stub-response [--status not_configured|noop] [--pretty] agent-machine steer serve-stub [--host 127.0.0.1] [--port 8080] [--status not_configured|noop] @@ -296,6 +297,7 @@ case "$COMMAND" in ;; registry) shift || true + delegate_python_cli registry "$@" if [ "${1:-}" = "resolve" ]; then shift || true delegate_registry_resolver "$@" diff --git a/scripts/resolve-agent-registry-grant.py b/scripts/resolve-agent-registry-grant.py old mode 100755 new mode 100644 diff --git a/scripts/validate-agent-registry.py b/scripts/validate-agent-registry.py old mode 100755 new mode 100644 diff --git a/scripts/validate-package.py b/scripts/validate-package.py index 5cab6c2..33ab161 100644 --- a/scripts/validate-package.py +++ b/scripts/validate-package.py @@ -16,6 +16,7 @@ def main() -> int: import agent_machine import agent_machine.activation + import agent_machine.agent_registry import agent_machine.cli import agent_machine.evidence import agent_machine.external_trust @@ -62,6 +63,8 @@ def main() -> int: raise AssertionError("unexpected release_bundle default repository") if agent_machine.policy_fabric.DEFAULT_DECIDED_AT != "1970-01-01T00:00:00Z": raise AssertionError("unexpected policy_fabric default decided_at") + if agent_machine.agent_registry.DEFAULT_ISSUED_AT != "1970-01-01T00:00:00Z": + raise AssertionError("unexpected agent_registry default issued_at") if getattr(registry_module, "DEFAULT_ISSUED_AT") != "1970-01-01T00:00:00Z": raise AssertionError("unexpected registry default issued_at") if str(default_model_cache_path()) != "/var/lib/agent-machine/models": diff --git a/src/agent_machine/cli.py b/src/agent_machine/cli.py index 9a79068..39dc668 100644 --- a/src/agent_machine/cli.py +++ b/src/agent_machine/cli.py @@ -315,6 +315,61 @@ def cmd_activate_evaluate(args: argparse.Namespace) -> int: return 0 +def cmd_registry_resolve(args: argparse.Namespace) -> int: + agent_registry = import_renderer(lambda: __import__("agent_machine.agent_registry", fromlist=["_unused"])) + agentpod = load_json(args.agentpod_json) + grants = agent_registry.load_agent_registry_grants(files=args.grant_file, directories=args.grant_dir, root=REPO_ROOT) + grant = agent_registry.resolve_agent_registry_grant( + grants=grants, + agentpod_id=str(agentpod.get("id")), + requested_agent_identity_ref=args.requested_agent_identity_ref, + session_ref=args.session_ref, + agent_machine_id=args.agent_machine_id, + workroom_ref=args.workroom_ref, + topic_ref=args.topic_ref, + grant_id=args.grant_id, + expected_status=args.expected_status, + allow_missing_stub=not args.no_missing_stub, + issued_at=args.issued_at, + requested_expires_at=args.requested_expires_at, + requested_scope=agent_registry.requested_scope_from_inputs( + provider_id=args.provider_id, + model_ref=args.model_ref, + tool_refs=args.tool_ref, + storage_scope_ref=args.storage_scope_ref, + evidence_scope_ref=args.evidence_scope_ref, + ), + root=REPO_ROOT, + ) + if args.pretty: + print(json.dumps(grant, indent=2, sort_keys=True)) + else: + print(json.dumps(grant, sort_keys=True, separators=(",", ":"))) + return 0 + + +def resolve_activation_policy(args: argparse.Namespace, agentpod: dict[str, Any], policy_fabric: Any) -> dict[str, Any]: + if args.policy_json is not None: + return load_json(args.policy_json) + policies = policy_fabric.load_policy_admissions(files=args.policy_file, directories=args.policy_dir, root=REPO_ROOT) + return policy_fabric.resolve_policy_admission( + policies=policies, + agentpod_id=str(agentpod.get("id")), + request_type="activation", + deployment_receipt_id=args.deployment_receipt_id, + agent_machine_id=args.agent_machine_id, + provider_id=args.provider_id, + policy_id=args.policy_id, + expected_status=args.expected_status, + allow_missing_stub=not args.no_missing_stub, + decided_at=args.decided_at, + root=REPO_ROOT, + ) + + +def resolve_activation_grant(args: argparse.Namespace, agentpod: dict[str, Any], agent_registry: Any) -> dict[str, Any]: + if args.grant_json is not None: + return load_json(args.grant_json) def resolve_activation_policy_and_grant(args: argparse.Namespace, agentpod: dict[str, Any], policy_fabric: Any) -> tuple[dict[str, Any], dict[str, Any]]: policy_json = args.policy_json grant_json = args.grant_json @@ -352,6 +407,23 @@ def resolve_registry_grant_from_args(args: argparse.Namespace, agentpod: dict[st return agent_registry.resolve_agent_registry_grant( grants=grants, agentpod_id=str(agentpod.get("id")), + requested_agent_identity_ref=args.requested_agent_identity_ref, + session_ref=args.session_ref, + agent_machine_id=args.agent_machine_id, + workroom_ref=args.workroom_ref, + topic_ref=args.topic_ref, + grant_id=args.grant_id, + expected_status=args.grant_expected_status, + allow_missing_stub=not args.no_missing_grant_stub, + issued_at=args.issued_at, + requested_expires_at=args.requested_expires_at, + requested_scope=agent_registry.requested_scope_from_inputs( + provider_id=args.provider_id, + model_ref=args.model_ref, + tool_refs=args.tool_ref, + storage_scope_ref=args.storage_scope_ref, + evidence_scope_ref=args.evidence_scope_ref, + ), requested_agent_identity_ref=requested_agent_identity_ref, session_ref=session_ref, agent_machine_id=args.agent_machine_id, @@ -367,6 +439,23 @@ def resolve_registry_grant_from_args(args: argparse.Namespace, agentpod: dict[st ) +def normalize_activation_positionals(args: argparse.Namespace) -> None: + """Preserve old shorthand while supporting policy/grant store resolution.""" + policy_resolver_requested = bool(args.policy_file or args.policy_dir or args.policy_id or args.expected_status) + grant_resolver_requested = bool(args.grant_file or args.grant_dir or args.grant_id or args.grant_expected_status) + if args.grant_json is None and args.policy_json is not None and policy_resolver_requested and not grant_resolver_requested: + args.grant_json = args.policy_json + args.policy_json = None + if args.grant_json is None and not grant_resolver_requested: + raise AssertionError( + "grant JSON is required. Use ` `, " + "` --policy-dir `, or provide --grant-dir/--grant-file." + ) + if args.policy_json is None and not policy_resolver_requested: + raise AssertionError( + "policy JSON is required. Use ` ` " + "or provide --policy-dir/--policy-file." + ) def cmd_registry_resolve(args: argparse.Namespace) -> int: agent_registry = import_renderer(lambda: __import__("agent_machine.agent_registry", fromlist=["_unused"])) agentpod = load_json(args.agentpod_json) @@ -430,6 +519,13 @@ def cmd_activate_evaluate(args: argparse.Namespace) -> int: policy_fabric = import_renderer(lambda: __import__("agent_machine.policy_fabric", fromlist=["_unused"])) agent_registry = import_renderer(lambda: __import__("agent_machine.agent_registry", fromlist=["_unused"])) agentpod = load_json(args.agentpod_json) + normalize_activation_positionals(args) + policy = resolve_activation_policy(args, agentpod, policy_fabric) + grant = resolve_activation_grant(args, agentpod, agent_registry) + storage_receipts = activation.load_storage_receipts( + files=args.storage_receipt_file, + directories=args.storage_receipt_dir, + ) policy, grant = resolve_activation_policy_and_grant(args, agentpod, policy_fabric) policy, grant = resolve_activation_policy_and_grant(args, agentpod, policy_fabric, agent_registry) storage_receipts = activation.load_storage_receipts(files=args.storage_receipt_file, directories=args.storage_receipt_dir) @@ -442,6 +538,11 @@ def cmd_activate_evaluate(args: argparse.Namespace) -> int: return 0 +def add_registry_resolver_args(parser: argparse.ArgumentParser) -> None: + parser.add_argument("--grant-file", action="append", type=Path, default=[]) + parser.add_argument("--grant-dir", action="append", type=Path, default=[]) + parser.add_argument("--requested-agent-identity-ref", required=True) + parser.add_argument("--session-ref", required=True) def cmd_steer_stub_response(args: argparse.Namespace) -> int: steering_stub = __import__("agent_machine.steering_stub", fromlist=["_unused"]) request = steering_stub.load_steer_request(str(args.request_json)) @@ -487,6 +588,7 @@ def add_registry_resolver_args(parser: argparse.ArgumentParser) -> None: parser.add_argument("--workroom-ref") parser.add_argument("--topic-ref") parser.add_argument("--grant-id") + parser.add_argument("--expected-status", choices=["missing", "active", "expired", "revoked", "denied", "unknown"]) parser.add_argument("--expected-grant-status", choices=["missing", "active", "expired", "revoked", "denied", "unknown"]) parser.add_argument("--no-missing-stub", action="store_true") parser.add_argument("--provider-id") @@ -496,6 +598,7 @@ def add_registry_resolver_args(parser: argparse.ArgumentParser) -> None: parser.add_argument("--evidence-scope-ref") parser.add_argument("--requested-expires-at") parser.add_argument("--issued-at", default="1970-01-01T00:00:00Z") + parser.add_argument("--pretty", action="store_true") def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Agent Machine Python CLI") subcommands = parser.add_subparsers(dest="command", required=True) @@ -547,11 +650,13 @@ def build_parser() -> argparse.ArgumentParser: policy_resolve.add_argument("--decided-at", default="1970-01-01T00:00:00Z") policy_resolve.add_argument("--pretty", action="store_true") policy_resolve.set_defaults(func=cmd_policy_resolve) + registry = subcommands.add_parser("registry", help="Resolve Agent Registry grant artifacts") registry_subcommands = registry.add_subparsers(dest="registry_command", required=True) registry_resolve = registry_subcommands.add_parser("resolve", help="Resolve an AgentRegistryGrant from local files/stores") registry_resolve.add_argument("agentpod_json", type=Path) add_registry_resolver_args(registry_resolve) + registry_resolve.set_defaults(func=cmd_registry_resolve) registry_resolve.add_argument("--pretty", action="store_true") registry_resolve.set_defaults(func=cmd_registry_resolve) activate = subcommands.add_parser("activate", help="Evaluate activation readiness") @@ -566,6 +671,23 @@ def build_parser() -> argparse.ArgumentParser: activate_evaluate.add_argument("--policy-id") activate_evaluate.add_argument("--expected-status", choices=["missing", "allowed", "denied", "not-required", "unknown"]) activate_evaluate.add_argument("--no-missing-stub", action="store_true") + activate_evaluate.add_argument("--grant-file", action="append", type=Path, default=[]) + activate_evaluate.add_argument("--grant-dir", action="append", type=Path, default=[]) + activate_evaluate.add_argument("--grant-id") + activate_evaluate.add_argument("--grant-expected-status", choices=["missing", "active", "expired", "revoked", "denied", "unknown"]) + activate_evaluate.add_argument("--no-missing-grant-stub", action="store_true") + activate_evaluate.add_argument("--requested-agent-identity-ref", default="urn:srcos:agent:local-inference-provider") + activate_evaluate.add_argument("--session-ref", default="urn:srcos:session:local-bootstrap") + activate_evaluate.add_argument("--workroom-ref") + activate_evaluate.add_argument("--topic-ref") + activate_evaluate.add_argument("--agent-machine-id") + activate_evaluate.add_argument("--provider-id") + activate_evaluate.add_argument("--model-ref") + activate_evaluate.add_argument("--tool-ref", action="append", default=[]) + activate_evaluate.add_argument("--storage-scope-ref") + activate_evaluate.add_argument("--evidence-scope-ref") + activate_evaluate.add_argument("--requested-expires-at") + activate_evaluate.add_argument("--issued-at", default="1970-01-01T00:00:00Z") activate_evaluate.add_argument("--agent-machine-id") activate_evaluate.add_argument("--provider-id") add_registry_resolver_args(activate_evaluate)