{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W6ndbrVAaywr"
      },
      "source": [
        "# EPI Recorder v3.0.0 - Google Colab Demo\n",
        "\n",
        "1.   List item\n",
        "2.   List item\n",
        "\n",
        "\n",
        "\n",
        "This notebook shows the full EPI product loop in Colab:\n",
        "\n",
        "- install `epi-recorder`\n",
        "- create a policy\n",
        "- record a good agent run\n",
        "- record a failing agent run\n",
        "- analyze policy violations\n",
        "- verify trust state\n",
        "- inspect the `.epi` contents\n",
        "- extract and render the embedded viewer\n",
        "- tamper with an artifact and prove trust drops to `NONE`\n",
        "\n",
        "The main flow works without any external model key. An optional final section also demonstrates the OpenAI Agents-style event bridge shipped in `v2.8.10`.\n",
        "\n",
        "For the branded investor walkthrough, use `EPI NEXUA VENTURES.ipynb`. This notebook is the cleaner technical backup.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mSzcjXubaywv"
      },
      "source": [
        "## 1. Install EPI Recorder\n",
        "\n",
        "This notebook is written for `epi-recorder==2.8.10`.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "noLvv31eaywv"
      },
      "outputs": [],
      "source": [
        "%pip install -q epi-recorder==2.8.10\n",
        "\n",
        "import epi_core\n",
        "print('Installed epi-recorder', epi_core.__version__)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "cSIK12-bayww"
      },
      "source": [
        "## 2. Set up the Colab workspace\n",
        "\n",
        "We keep everything under `/content/epi_colab_demo` so the artifacts are easy to inspect and download.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "S3UsBM_aaywx"
      },
      "outputs": [],
      "source": [
        "import json\n",
        "import os\n",
        "import shutil\n",
        "import subprocess\n",
        "import sys\n",
        "import zipfile\n",
        "from pathlib import Path\n",
        "\n",
        "WORK = Path('/content/epi_colab_demo')\n",
        "if WORK.exists():\n",
        "    shutil.rmtree(WORK)\n",
        "WORK.mkdir(parents=True, exist_ok=True)\n",
        "os.chdir(WORK)\n",
        "\n",
        "ARTIFACTS = WORK / 'artifacts'\n",
        "ARTIFACTS.mkdir(exist_ok=True)\n",
        "\n",
        "print('Workspace:', WORK)\n",
        "print('Artifacts:', ARTIFACTS)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GJNjQqwUaywx"
      },
      "source": [
        "## 3. Create a policy rulebook\n",
        "\n",
        "This policy is intentionally business-shaped. It says:\n",
        "\n",
        "- manager approval is required before refund approval\n",
        "- only approved tools may be used\n",
        "- refunds above the threshold need explicit approval quality\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "DbdTrzHlaywy"
      },
      "outputs": [],
      "source": [
        "policy = {\n",
        "    'policy_format_version': '2.0',\n",
        "    'policy_id': 'refund-demo-v2',\n",
        "    'system_name': 'refund-agent-demo',\n",
        "    'system_version': '2.8.10',\n",
        "    'policy_version': '2026-03-24',\n",
        "    'scope': {\n",
        "        'environment': 'demo',\n",
        "        'workflow': 'customer_refund',\n",
        "        'team': 'support-ops'\n",
        "    },\n",
        "    'approval_policies': [\n",
        "        {\n",
        "            'approval_id': 'manager-refund-approval',\n",
        "            'required_roles': ['manager'],\n",
        "            'minimum_approvers': 1,\n",
        "            'reason_required': True\n",
        "        }\n",
        "    ],\n",
        "    'rules': [\n",
        "        {\n",
        "            'id': 'R005',\n",
        "            'name': 'Manager Approval Before Refund',\n",
        "            'severity': 'critical',\n",
        "            'description': 'Refund approval requires an approved manager response.',\n",
        "            'type': 'approval_guard',\n",
        "            'mode': 'require_approval',\n",
        "            'applies_at': ['decision'],\n",
        "            'approval_action': 'approve_refund',\n",
        "            'approval_policy_ref': 'manager-refund-approval'\n",
        "        },\n",
        "        {\n",
        "            'id': 'R006',\n",
        "            'name': 'Only Approved Refund Tools',\n",
        "            'severity': 'high',\n",
        "            'description': 'Only allowlisted tools may be called in this workflow.',\n",
        "            'type': 'tool_permission_guard',\n",
        "            'mode': 'detect',\n",
        "            'applies_at': ['tool_call'],\n",
        "            'allowed_tools': ['lookup_order', 'verify_identity', 'issue_refund', 'send_email']\n",
        "        }\n",
        "    ]\n",
        "}\n",
        "\n",
        "policy_path = WORK / 'epi_policy.json'\n",
        "policy_path.write_text(json.dumps(policy, indent=2), encoding='utf-8')\n",
        "print(policy_path.read_text())\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LRsq2CuBaywy"
      },
      "source": [
        "## 4. Validate the policy with the CLI\n",
        "\n",
        "This exercises the improved `epi policy validate` diagnostics shipped in `v2.8.10`.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2QAYCjoYaywz"
      },
      "outputs": [],
      "source": [
        "subprocess.run([sys.executable, '-m', 'epi_cli.main', 'policy', 'validate', 'epi_policy.json'], check=True)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PE_4ofzAaywz"
      },
      "source": [
        "## 5. Record a good agent run and a failing agent run\n",
        "\n",
        "We use the Python API directly so this notebook works reliably in Colab.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "IPMKXsycayw0"
      },
      "outputs": [],
      "source": [
        "from epi_recorder import record\n",
        "\n",
        "\n",
        "def make_good_run(path: Path):\n",
        "    with record(path, workflow_name='Refund Agent Demo', goal='Resolve a customer refund safely') as epi:\n",
        "        with epi.agent_run(\n",
        "            'refund-agent',\n",
        "            user_input='Refund order ORD-1001',\n",
        "            session_id='sess-good-001',\n",
        "            task_id='refund-ORD-1001',\n",
        "            attempt=1,\n",
        "        ) as agent:\n",
        "            agent.plan('Look up the order, verify identity, request approval, then decide.')\n",
        "            agent.message('user', 'Refund order ORD-1001')\n",
        "            agent.memory_read('refund_history', query='ORD-1001', result_count=1)\n",
        "            agent.tool_call('lookup_order', {'order_id': 'ORD-1001'})\n",
        "            agent.tool_result('lookup_order', {'status': 'paid', 'amount': 120, 'customer_id': 'CUST-42'})\n",
        "            agent.tool_call('verify_identity', {'customer_id': 'CUST-42'})\n",
        "            agent.tool_result('verify_identity', {'verified': True})\n",
        "            agent.approval_request('approve_refund', reason='Manual approval required for refund action')\n",
        "            agent.approval_response('approve_refund', approved=True, reviewer='manager', notes='Approved after identity verification')\n",
        "            agent.decision('approve_refund', confidence=0.94)\n",
        "            agent.tool_call('issue_refund', {'order_id': 'ORD-1001', 'amount': 120})\n",
        "            agent.tool_result('issue_refund', {'success': True, 'refund_id': 'RF-1001'})\n",
        "\n",
        "\n",
        "def make_bad_run(path: Path):\n",
        "    with record(path, workflow_name='Refund Agent Demo', goal='Resolve a customer refund safely') as epi:\n",
        "        with epi.agent_run(\n",
        "            'refund-agent',\n",
        "            user_input='Refund order ORD-2002',\n",
        "            session_id='sess-bad-001',\n",
        "            task_id='refund-ORD-2002',\n",
        "            attempt=1,\n",
        "        ) as agent:\n",
        "            agent.plan('Look up the order, then decide quickly.')\n",
        "            agent.message('user', 'Refund order ORD-2002')\n",
        "            agent.tool_call('lookup_order', {'order_id': 'ORD-2002'})\n",
        "            agent.tool_result('lookup_order', {'status': 'paid', 'amount': 500, 'customer_id': 'CUST-77'})\n",
        "            agent.tool_call('admin_override', {'order_id': 'ORD-2002'})\n",
        "            agent.tool_result('admin_override', {'status': 'override used'})\n",
        "            agent.decision('approve_refund', confidence=0.62)\n",
        "            agent.tool_call('issue_refund', {'order_id': 'ORD-2002', 'amount': 500})\n",
        "            agent.tool_result('issue_refund', {'success': True, 'refund_id': 'RF-2002'})\n",
        "\n",
        "\n",
        "good_artifact = ARTIFACTS / 'refund_agent_ok.epi'\n",
        "bad_artifact = ARTIFACTS / 'refund_agent_missing_approval.epi'\n",
        "\n",
        "make_good_run(good_artifact)\n",
        "make_bad_run(bad_artifact)\n",
        "\n",
        "print('Created:', good_artifact)\n",
        "print('Created:', bad_artifact)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "g-hyFzWoayw0"
      },
      "source": [
        "## 6. Analyze and verify both artifacts\n",
        "\n",
        "The good artifact should pass cleanly. The bad artifact should show policy failures.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Oyskl1YGayw0"
      },
      "outputs": [],
      "source": [
        "def run_cli(*args):\n",
        "    print('$ epi', ' '.join(args))\n",
        "    result = subprocess.run([sys.executable, '-m', 'epi_cli.main', *args], capture_output=True, text=True)\n",
        "    print(result.stdout)\n",
        "    if result.stderr:\n",
        "        print(result.stderr)\n",
        "    print('exit_code =', result.returncode)\n",
        "    print('-' * 80)\n",
        "    return result\n",
        "\n",
        "\n",
        "run_cli('analyze', str(good_artifact))\n",
        "run_cli('verify', str(good_artifact))\n",
        "run_cli('analyze', str(bad_artifact))\n",
        "run_cli('verify', str(bad_artifact))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "M9oKGAQsayw1"
      },
      "source": [
        "## 7. Inspect the sealed contents inside a `.epi` file\n",
        "\n",
        "This shows why EPI is more than logs: policy, analysis, trust metadata, and the embedded viewer are all inside one artifact.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rK1QCr7Bayw1"
      },
      "outputs": [],
      "source": [
        "def inspect_epi(path: Path):\n",
        "    print('=' * 80)\n",
        "    print(path.name)\n",
        "    with zipfile.ZipFile(path, 'r') as zf:\n",
        "        names = sorted(zf.namelist())\n",
        "        for name in names:\n",
        "            print(' -', name)\n",
        "        manifest = json.loads(zf.read('manifest.json').decode('utf-8'))\n",
        "        print('\\nspec_version =', manifest.get('spec_version'))\n",
        "        print('signature_present =', bool(manifest.get('signature')))\n",
        "        if 'analysis.json' in names:\n",
        "            analysis = json.loads(zf.read('analysis.json').decode('utf-8'))\n",
        "            print('fault_detected =', analysis.get('fault_detected'))\n",
        "            print('headline =', analysis.get('summary', {}).get('headline'))\n",
        "        if 'policy_evaluation.json' in names:\n",
        "            evaluation = json.loads(zf.read('policy_evaluation.json').decode('utf-8'))\n",
        "            print('controls_evaluated =', evaluation.get('controls_evaluated'))\n",
        "            print('controls_failed =', evaluation.get('controls_failed'))\n",
        "            for item in evaluation.get('results', [])[:3]:\n",
        "                print('  *', item.get('rule_id'), item.get('status'))\n",
        "\n",
        "\n",
        "inspect_epi(good_artifact)\n",
        "inspect_epi(bad_artifact)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SDD3vJ1Zayw1"
      },
      "source": [
        "## 8. Extract and render the embedded viewer\n",
        "\n",
        "This uses `epi view --extract` and renders the generated `viewer.html` inside an iframe so Colab shows the real embedded artifact viewer.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "hQrZJwdhayw2"
      },
      "outputs": [],
      "source": [
        "from IPython.display import HTML, display\n",
        "import html as html_lib\n",
        "\n",
        "viewer_dir = WORK / 'bad_viewer'\n",
        "subprocess.run(\n",
        "    [sys.executable, '-m', 'epi_cli.main', 'view', str(bad_artifact), '--extract', str(viewer_dir)],\n",
        "    check=True,\n",
        ")\n",
        "\n",
        "viewer_html = (viewer_dir / 'viewer.html').read_text(encoding='utf-8')\n",
        "iframe_srcdoc = html_lib.escape(viewer_html, quote=True)\n",
        "display(HTML(f'<iframe srcdoc=\"{iframe_srcdoc}\" style=\"width:100%;height:980px;border:1px solid #d1d5db;border-radius:18px;background:white;box-shadow:0 10px 30px rgba(0,0,0,0.06);\"></iframe>'))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "deEY7mANayw2"
      },
      "source": [
        "## 9. Tamper with an artifact and prove trust drops\n",
        "\n",
        "This modifies `analysis.json` inside a copied artifact. The content may still open, but trust must drop to `NONE`.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vrD-QBm2ayw2"
      },
      "outputs": [],
      "source": [
        "tampered_artifact = ARTIFACTS / 'refund_agent_missing_approval_tampered.epi'\n",
        "shutil.copy2(bad_artifact, tampered_artifact)\n",
        "\n",
        "with zipfile.ZipFile(tampered_artifact, 'a') as zf:\n",
        "    analysis = json.loads(zf.read('analysis.json').decode('utf-8'))\n",
        "    analysis['summary']['headline'] = 'FORGED ANALYSIS - DO NOT TRUST'\n",
        "    zf.writestr('analysis.json', json.dumps(analysis, indent=2))\n",
        "\n",
        "run_cli('verify', str(bad_artifact))\n",
        "run_cli('verify', str(tampered_artifact))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PiNtwCxuayw2"
      },
      "source": [
        "## 10. Optional: demonstrate the OpenAI Agents-style event bridge\n",
        "\n",
        "This does not require the OpenAI Agents SDK. We feed synthetic stream events into `OpenAIAgentsRecorder` so you can see the `v2.8.10` bridge surface in a notebook-friendly way.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JRImHFHOayw2"
      },
      "outputs": [],
      "source": [
        "from epi_recorder.integrations import OpenAIAgentsRecorder\n",
        "\n",
        "bridge_artifact = ARTIFACTS / 'openai_agents_bridge_demo.epi'\n",
        "synthetic_events = [\n",
        "    {'type': 'run_item_stream_event', 'item': {'type': 'message', 'role': 'assistant', 'content': 'I will reset the password safely.'}},\n",
        "    {'type': 'run_item_stream_event', 'item': {'type': 'tool_call', 'tool_name': 'lookup_user', 'arguments': {'email': 'customer@example.com'}, 'call_id': 'call-1'}},\n",
        "    {'type': 'run_item_stream_event', 'item': {'type': 'tool_result', 'tool_name': 'lookup_user', 'result': {'user_id': 'user-123'}, 'call_id': 'call-1'}},\n",
        "    {'type': 'run_item_stream_event', 'item': {'type': 'approval_request', 'action': 'reset_password', 'reason': 'High-risk account action'}},\n",
        "    {'type': 'run_item_stream_event', 'item': {'type': 'approval_response', 'action': 'reset_password', 'approved': True, 'reviewer': 'manager', 'notes': 'Approved'}},\n",
        "    {'type': 'run_item_stream_event', 'item': {'type': 'final_output', 'output': 'Password reset completed successfully.'}},\n",
        "]\n",
        "\n",
        "with record(bridge_artifact, workflow_name='OpenAI Agents Bridge Demo') as epi:\n",
        "    with OpenAIAgentsRecorder(epi, agent_name='support-agent', user_input='Reset customer password') as recorder:\n",
        "        for event in synthetic_events:\n",
        "            recorder.consume(event)\n",
        "\n",
        "inspect_epi(bridge_artifact)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7AUfJg7Sayw3"
      },
      "source": [
        "## 11. Download artifacts from Colab\n",
        "\n",
        "Use this if you want to pull the generated `.epi` files onto your machine.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "xzBQbinaayw3"
      },
      "outputs": [],
      "source": [
        "from google.colab import files\n",
        "\n",
        "print('Available artifacts:')\n",
        "for path in sorted(ARTIFACTS.glob('*.epi')):\n",
        "    print(' -', path.name)\n",
        "\n",
        "# Uncomment one of these to download a file:\n",
        "# files.download(str(good_artifact))\n",
        "# files.download(str(bad_artifact))\n",
        "# files.download(str(tampered_artifact))\n",
        "# files.download(str(bridge_artifact))\n"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}