Created
March 11, 2026 22:48
-
-
Save mkuchak/fa30100485e27b967ed5d06bf09a31ad to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Agent Action Execution Layer — Architecture Plan</title> | |
| <!-- Fonts --> | |
| <link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=clash-display@400,500,600,700&f[]=satoshi@400,500,700&display=swap"> | |
| <style> | |
| /* =========================================== | |
| CSS CUSTOM PROPERTIES (THEME) | |
| Dark tech aesthetic with cyan accent | |
| =========================================== */ | |
| :root { | |
| /* Colors */ | |
| --bg-primary: #080c14; | |
| --bg-secondary: #0e1420; | |
| --bg-card: #111827; | |
| --bg-card-hover: #1a2236; | |
| --border-subtle: rgba(0, 230, 200, 0.08); | |
| --border-accent: rgba(0, 230, 200, 0.2); | |
| --text-primary: #f0f4f8; | |
| --text-secondary: #8899aa; | |
| --text-muted: #556677; | |
| --accent: #00e6c8; | |
| --accent-dim: rgba(0, 230, 200, 0.15); | |
| --accent-glow: rgba(0, 230, 200, 0.25); | |
| --red: #ff5c6e; | |
| --red-dim: rgba(255, 92, 110, 0.12); | |
| --amber: #ffb347; | |
| --amber-dim: rgba(255, 179, 71, 0.12); | |
| --green: #4ade80; | |
| --green-dim: rgba(74, 222, 128, 0.12); | |
| --blue: #60a5fa; | |
| --blue-dim: rgba(96, 165, 250, 0.12); | |
| --purple: #a78bfa; | |
| --purple-dim: rgba(167, 139, 250, 0.12); | |
| /* Typography */ | |
| --font-display: 'Clash Display', sans-serif; | |
| --font-body: 'Satoshi', sans-serif; | |
| --title-size: clamp(2rem, 5.5vw, 4.5rem); | |
| --h2-size: clamp(1.4rem, 3.5vw, 2.8rem); | |
| --h3-size: clamp(1rem, 2vw, 1.5rem); | |
| --body-size: clamp(0.75rem, 1.3vw, 1.05rem); | |
| --small-size: clamp(0.65rem, 1vw, 0.85rem); | |
| --code-size: clamp(0.65rem, 0.9vw, 0.82rem); | |
| /* Spacing */ | |
| --slide-padding: clamp(1.5rem, 5vw, 5rem); | |
| --content-gap: clamp(0.75rem, 2vw, 2rem); | |
| --element-gap: clamp(0.3rem, 0.8vw, 0.75rem); | |
| /* Animation */ | |
| --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); | |
| --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); | |
| --duration-normal: 0.7s; | |
| --duration-fast: 0.4s; | |
| } | |
| /* =========================================== | |
| BASE STYLES | |
| =========================================== */ | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| html { | |
| scroll-behavior: smooth; | |
| scroll-snap-type: y mandatory; | |
| height: 100%; | |
| overflow-x: hidden; | |
| } | |
| body { | |
| font-family: var(--font-body); | |
| background: var(--bg-primary); | |
| color: var(--text-primary); | |
| overflow-x: hidden; | |
| height: 100%; | |
| -webkit-font-smoothing: antialiased; | |
| } | |
| /* =========================================== | |
| SLIDE CONTAINER | |
| Each slide = exactly one viewport height | |
| =========================================== */ | |
| .slide { | |
| width: 100vw; | |
| height: 100vh; | |
| height: 100dvh; | |
| padding: var(--slide-padding); | |
| scroll-snap-align: start; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .slide-content { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| max-height: 100%; | |
| overflow: hidden; | |
| max-width: 1300px; | |
| margin: 0 auto; | |
| width: 100%; | |
| } | |
| /* =========================================== | |
| BACKGROUND EFFECTS | |
| =========================================== */ | |
| .slide::before { | |
| content: ''; | |
| position: absolute; | |
| inset: 0; | |
| background: | |
| radial-gradient(ellipse 60% 50% at 80% 20%, rgba(0, 230, 200, 0.03) 0%, transparent 70%), | |
| radial-gradient(ellipse 40% 60% at 10% 80%, rgba(96, 165, 250, 0.02) 0%, transparent 70%); | |
| pointer-events: none; | |
| } | |
| /* Grid pattern */ | |
| .grid-bg::after { | |
| content: ''; | |
| position: absolute; | |
| inset: 0; | |
| background-image: | |
| linear-gradient(rgba(0, 230, 200, 0.03) 1px, transparent 1px), | |
| linear-gradient(90deg, rgba(0, 230, 200, 0.03) 1px, transparent 1px); | |
| background-size: 60px 60px; | |
| pointer-events: none; | |
| mask-image: radial-gradient(ellipse 70% 70% at 50% 50%, black 30%, transparent 80%); | |
| -webkit-mask-image: radial-gradient(ellipse 70% 70% at 50% 50%, black 30%, transparent 80%); | |
| } | |
| /* =========================================== | |
| TYPOGRAPHY | |
| =========================================== */ | |
| h1 { | |
| font-family: var(--font-display); | |
| font-size: var(--title-size); | |
| font-weight: 700; | |
| line-height: 1.05; | |
| letter-spacing: -0.02em; | |
| } | |
| h2 { | |
| font-family: var(--font-display); | |
| font-size: var(--h2-size); | |
| font-weight: 600; | |
| line-height: 1.15; | |
| letter-spacing: -0.015em; | |
| margin-bottom: var(--content-gap); | |
| } | |
| h3 { | |
| font-family: var(--font-display); | |
| font-size: var(--h3-size); | |
| font-weight: 500; | |
| line-height: 1.3; | |
| color: var(--accent); | |
| } | |
| p, li { | |
| font-size: var(--body-size); | |
| line-height: 1.55; | |
| color: var(--text-secondary); | |
| } | |
| .accent { color: var(--accent); } | |
| .red { color: var(--red); } | |
| .amber { color: var(--amber); } | |
| .green { color: var(--green); } | |
| .blue { color: var(--blue); } | |
| .muted { color: var(--text-muted); } | |
| .white { color: var(--text-primary); } | |
| strong { color: var(--text-primary); font-weight: 600; } | |
| /* =========================================== | |
| LABEL / TAG | |
| =========================================== */ | |
| .label { | |
| display: inline-block; | |
| font-family: var(--font-body); | |
| font-size: var(--small-size); | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: 0.12em; | |
| padding: 0.3em 0.9em; | |
| border-radius: 999px; | |
| background: var(--accent-dim); | |
| color: var(--accent); | |
| margin-bottom: var(--content-gap); | |
| } | |
| .label.red-label { | |
| background: var(--red-dim); | |
| color: var(--red); | |
| } | |
| .label.amber-label { | |
| background: var(--amber-dim); | |
| color: var(--amber); | |
| } | |
| .label.green-label { | |
| background: var(--green-dim); | |
| color: var(--green); | |
| } | |
| .label.blue-label { | |
| background: var(--blue-dim); | |
| color: var(--blue); | |
| } | |
| .label.purple-label { | |
| background: var(--purple-dim); | |
| color: var(--purple); | |
| } | |
| /* =========================================== | |
| CARDS & CONTAINERS | |
| =========================================== */ | |
| .card { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-subtle); | |
| border-radius: clamp(8px, 1vw, 14px); | |
| padding: clamp(0.8rem, 1.5vw, 1.5rem); | |
| transition: border-color 0.3s ease, background 0.3s ease; | |
| } | |
| .card:hover { | |
| border-color: var(--border-accent); | |
| background: var(--bg-card-hover); | |
| } | |
| .card h3 { | |
| margin-bottom: var(--element-gap); | |
| } | |
| /* =========================================== | |
| GRID LAYOUTS | |
| =========================================== */ | |
| .grid-2 { | |
| display: grid; | |
| grid-template-columns: repeat(2, 1fr); | |
| gap: clamp(0.6rem, 1.5vw, 1.2rem); | |
| } | |
| .grid-3 { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: clamp(0.6rem, 1.5vw, 1.2rem); | |
| } | |
| .grid-4 { | |
| display: grid; | |
| grid-template-columns: repeat(4, 1fr); | |
| gap: clamp(0.5rem, 1vw, 1rem); | |
| } | |
| @media (max-width: 900px) { | |
| .grid-3 { grid-template-columns: repeat(2, 1fr); } | |
| .grid-4 { grid-template-columns: repeat(2, 1fr); } | |
| } | |
| @media (max-width: 600px) { | |
| .grid-2 { grid-template-columns: 1fr; } | |
| .grid-3 { grid-template-columns: 1fr; } | |
| .grid-4 { grid-template-columns: 1fr; } | |
| } | |
| /* =========================================== | |
| CODE BLOCKS | |
| =========================================== */ | |
| .code-block { | |
| background: #0a0f1a; | |
| border: 1px solid var(--border-subtle); | |
| border-radius: clamp(6px, 0.8vw, 10px); | |
| padding: clamp(0.6rem, 1.2vw, 1.2rem); | |
| font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace; | |
| font-size: var(--code-size); | |
| line-height: 1.6; | |
| color: var(--text-secondary); | |
| overflow: hidden; | |
| white-space: pre; | |
| } | |
| .code-block .keyword { color: var(--purple); } | |
| .code-block .string { color: var(--green); } | |
| .code-block .comment { color: var(--text-muted); font-style: italic; } | |
| .code-block .fn { color: var(--blue); } | |
| .code-block .accent { color: var(--accent); } | |
| .code-block .type { color: var(--amber); } | |
| code { | |
| font-family: 'SF Mono', 'Fira Code', monospace; | |
| font-size: 0.9em; | |
| background: var(--accent-dim); | |
| color: var(--accent); | |
| padding: 0.15em 0.45em; | |
| border-radius: 4px; | |
| } | |
| /* =========================================== | |
| FLOW DIAGRAM | |
| =========================================== */ | |
| .flow { | |
| display: flex; | |
| align-items: center; | |
| gap: clamp(0.3rem, 0.8vw, 0.8rem); | |
| flex-wrap: wrap; | |
| } | |
| .flow-step { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-subtle); | |
| border-radius: 8px; | |
| padding: clamp(0.4rem, 0.8vw, 0.7rem) clamp(0.6rem, 1vw, 1rem); | |
| font-size: var(--small-size); | |
| font-weight: 500; | |
| color: var(--text-primary); | |
| white-space: nowrap; | |
| } | |
| .flow-step.active { | |
| border-color: var(--accent); | |
| background: var(--accent-dim); | |
| color: var(--accent); | |
| } | |
| .flow-arrow { | |
| color: var(--text-muted); | |
| font-size: var(--small-size); | |
| } | |
| /* =========================================== | |
| TABLE | |
| =========================================== */ | |
| .table-wrap { | |
| overflow: hidden; | |
| border-radius: clamp(6px, 0.8vw, 10px); | |
| border: 1px solid var(--border-subtle); | |
| } | |
| table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| font-size: var(--small-size); | |
| } | |
| th { | |
| background: var(--bg-card); | |
| color: var(--accent); | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: 0.06em; | |
| padding: clamp(0.4rem, 0.8vw, 0.7rem) clamp(0.5rem, 1vw, 1rem); | |
| text-align: left; | |
| border-bottom: 1px solid var(--border-accent); | |
| font-size: var(--small-size); | |
| } | |
| td { | |
| padding: clamp(0.35rem, 0.7vw, 0.6rem) clamp(0.5rem, 1vw, 1rem); | |
| border-bottom: 1px solid var(--border-subtle); | |
| color: var(--text-secondary); | |
| vertical-align: top; | |
| } | |
| tr:last-child td { border-bottom: none; } | |
| tr:hover td { background: rgba(0, 230, 200, 0.02); } | |
| /* =========================================== | |
| STATUS BADGES | |
| =========================================== */ | |
| .badge { | |
| display: inline-block; | |
| font-size: clamp(0.55rem, 0.75vw, 0.72rem); | |
| font-weight: 600; | |
| padding: 0.2em 0.6em; | |
| border-radius: 4px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.04em; | |
| } | |
| .badge-red { background: var(--red-dim); color: var(--red); } | |
| .badge-green { background: var(--green-dim); color: var(--green); } | |
| .badge-amber { background: var(--amber-dim); color: var(--amber); } | |
| .badge-blue { background: var(--blue-dim); color: var(--blue); } | |
| .badge-accent { background: var(--accent-dim); color: var(--accent); } | |
| /* =========================================== | |
| BULLET LIST | |
| =========================================== */ | |
| .bullet-list { | |
| list-style: none; | |
| display: flex; | |
| flex-direction: column; | |
| gap: var(--element-gap); | |
| } | |
| .bullet-list li { | |
| padding-left: 1.4em; | |
| position: relative; | |
| } | |
| .bullet-list li::before { | |
| content: ''; | |
| position: absolute; | |
| left: 0; | |
| top: 0.55em; | |
| width: 6px; | |
| height: 6px; | |
| border-radius: 50%; | |
| background: var(--accent); | |
| } | |
| /* =========================================== | |
| LAYER INDICATOR | |
| =========================================== */ | |
| .layer-indicator { | |
| display: flex; | |
| gap: clamp(0.3rem, 0.6vw, 0.5rem); | |
| margin-bottom: var(--content-gap); | |
| } | |
| .layer-dot { | |
| width: clamp(28px, 3vw, 44px); | |
| height: clamp(4px, 0.4vw, 6px); | |
| border-radius: 999px; | |
| background: var(--border-subtle); | |
| transition: background 0.4s ease; | |
| } | |
| .layer-dot.active { background: var(--accent); } | |
| /* =========================================== | |
| SEPARATOR | |
| =========================================== */ | |
| .sep { | |
| width: 60px; | |
| height: 3px; | |
| border-radius: 999px; | |
| background: var(--accent); | |
| margin: var(--content-gap) 0; | |
| opacity: 0.6; | |
| } | |
| /* =========================================== | |
| TITLE SLIDE SPECIFIC | |
| =========================================== */ | |
| .title-slide .subtitle { | |
| font-size: clamp(0.9rem, 1.8vw, 1.35rem); | |
| color: var(--text-secondary); | |
| margin-top: clamp(0.5rem, 1.2vw, 1.2rem); | |
| font-weight: 400; | |
| max-width: 700px; | |
| } | |
| .title-slide .meta { | |
| font-size: var(--small-size); | |
| color: var(--text-muted); | |
| margin-top: clamp(1rem, 2vw, 2rem); | |
| display: flex; | |
| gap: clamp(1rem, 2vw, 2rem); | |
| } | |
| .title-slide .meta span { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.4em; | |
| } | |
| /* Glowing accent line under title */ | |
| .title-accent { | |
| width: clamp(60px, 8vw, 120px); | |
| height: 3px; | |
| background: linear-gradient(90deg, var(--accent), transparent); | |
| border-radius: 999px; | |
| margin-top: clamp(0.8rem, 1.5vw, 1.5rem); | |
| box-shadow: 0 0 20px var(--accent-glow); | |
| } | |
| /* =========================================== | |
| SPLIT LAYOUT | |
| =========================================== */ | |
| .split { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: clamp(1.5rem, 3vw, 3rem); | |
| align-items: center; | |
| } | |
| @media (max-width: 800px) { | |
| .split { grid-template-columns: 1fr; } | |
| } | |
| /* =========================================== | |
| VISUAL FLOW DIAGRAM (VERTICAL) | |
| =========================================== */ | |
| .v-flow { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0; | |
| position: relative; | |
| } | |
| .v-flow-step { | |
| display: flex; | |
| align-items: flex-start; | |
| gap: clamp(0.6rem, 1.2vw, 1.2rem); | |
| position: relative; | |
| padding-bottom: clamp(0.5rem, 1vh, 1rem); | |
| } | |
| .v-flow-num { | |
| width: clamp(24px, 2.5vw, 34px); | |
| height: clamp(24px, 2.5vw, 34px); | |
| border-radius: 50%; | |
| background: var(--accent-dim); | |
| border: 1px solid var(--accent); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: var(--small-size); | |
| font-weight: 700; | |
| color: var(--accent); | |
| flex-shrink: 0; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .v-flow-step:not(:last-child)::before { | |
| content: ''; | |
| position: absolute; | |
| left: calc(clamp(24px, 2.5vw, 34px) / 2 - 1px); | |
| top: clamp(24px, 2.5vw, 34px); | |
| bottom: 0; | |
| width: 2px; | |
| background: var(--border-accent); | |
| } | |
| .v-flow-content h3 { | |
| font-size: var(--body-size); | |
| margin-bottom: 0.2em; | |
| } | |
| .v-flow-content p { | |
| font-size: var(--small-size); | |
| color: var(--text-muted); | |
| } | |
| /* =========================================== | |
| ANIMATIONS | |
| =========================================== */ | |
| .reveal { | |
| opacity: 0; | |
| transform: translateY(25px); | |
| transition: opacity var(--duration-normal) var(--ease-out-expo), | |
| transform var(--duration-normal) var(--ease-out-expo); | |
| } | |
| .reveal-left { | |
| opacity: 0; | |
| transform: translateX(-30px); | |
| transition: opacity var(--duration-normal) var(--ease-out-expo), | |
| transform var(--duration-normal) var(--ease-out-expo); | |
| } | |
| .reveal-right { | |
| opacity: 0; | |
| transform: translateX(30px); | |
| transition: opacity var(--duration-normal) var(--ease-out-expo), | |
| transform var(--duration-normal) var(--ease-out-expo); | |
| } | |
| .reveal-scale { | |
| opacity: 0; | |
| transform: scale(0.92); | |
| transition: opacity var(--duration-normal) var(--ease-out-expo), | |
| transform var(--duration-normal) var(--ease-out-expo); | |
| } | |
| .slide.visible .reveal, | |
| .slide.visible .reveal-left, | |
| .slide.visible .reveal-right, | |
| .slide.visible .reveal-scale { | |
| opacity: 1; | |
| transform: translateY(0) translateX(0) scale(1); | |
| } | |
| /* Stagger children */ | |
| .stagger > :nth-child(1) { transition-delay: 0.05s; } | |
| .stagger > :nth-child(2) { transition-delay: 0.12s; } | |
| .stagger > :nth-child(3) { transition-delay: 0.19s; } | |
| .stagger > :nth-child(4) { transition-delay: 0.26s; } | |
| .stagger > :nth-child(5) { transition-delay: 0.33s; } | |
| .stagger > :nth-child(6) { transition-delay: 0.40s; } | |
| .stagger > :nth-child(7) { transition-delay: 0.47s; } | |
| .stagger > :nth-child(8) { transition-delay: 0.54s; } | |
| /* =========================================== | |
| PROGRESS BAR | |
| =========================================== */ | |
| .progress-bar { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| height: 3px; | |
| background: linear-gradient(90deg, var(--accent), var(--blue)); | |
| z-index: 1000; | |
| transition: width 0.3s var(--ease-out-quart); | |
| box-shadow: 0 0 10px var(--accent-glow); | |
| } | |
| /* =========================================== | |
| NAV DOTS | |
| =========================================== */ | |
| .nav-dots { | |
| position: fixed; | |
| right: clamp(10px, 1.5vw, 24px); | |
| top: 50%; | |
| transform: translateY(-50%); | |
| display: flex; | |
| flex-direction: column; | |
| gap: clamp(6px, 0.8vh, 10px); | |
| z-index: 100; | |
| } | |
| .nav-dot { | |
| width: clamp(6px, 0.5vw, 8px); | |
| height: clamp(6px, 0.5vw, 8px); | |
| border-radius: 50%; | |
| background: var(--text-muted); | |
| opacity: 0.3; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| border: none; | |
| padding: 0; | |
| } | |
| .nav-dot:hover { opacity: 0.6; } | |
| .nav-dot.active { | |
| opacity: 1; | |
| background: var(--accent); | |
| box-shadow: 0 0 8px var(--accent-glow); | |
| transform: scale(1.3); | |
| } | |
| /* =========================================== | |
| SLIDE COUNTER | |
| =========================================== */ | |
| .slide-counter { | |
| position: fixed; | |
| bottom: clamp(12px, 1.5vw, 24px); | |
| right: clamp(16px, 2vw, 32px); | |
| font-family: var(--font-body); | |
| font-size: var(--small-size); | |
| color: var(--text-muted); | |
| z-index: 100; | |
| font-variant-numeric: tabular-nums; | |
| } | |
| .slide-counter .current { color: var(--accent); font-weight: 600; } | |
| /* =========================================== | |
| KEYBOARD HINT | |
| =========================================== */ | |
| .keyboard-hint { | |
| position: fixed; | |
| bottom: clamp(12px, 1.5vw, 24px); | |
| left: clamp(16px, 2vw, 32px); | |
| font-size: clamp(0.6rem, 0.8vw, 0.72rem); | |
| color: var(--text-muted); | |
| opacity: 0.4; | |
| z-index: 100; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5em; | |
| } | |
| .key { | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| min-width: 1.6em; | |
| padding: 0.15em 0.35em; | |
| border: 1px solid var(--text-muted); | |
| border-radius: 3px; | |
| font-size: 0.9em; | |
| line-height: 1; | |
| } | |
| /* =========================================== | |
| RESPONSIVE BREAKPOINTS | |
| =========================================== */ | |
| @media (max-height: 700px) { | |
| :root { | |
| --slide-padding: clamp(1rem, 3.5vw, 2.5rem); | |
| --content-gap: clamp(0.5rem, 1.5vw, 1.2rem); | |
| --title-size: clamp(1.5rem, 5vw, 3rem); | |
| --h2-size: clamp(1.2rem, 3vw, 2rem); | |
| } | |
| } | |
| @media (max-height: 600px) { | |
| :root { | |
| --slide-padding: clamp(0.75rem, 3vw, 2rem); | |
| --content-gap: clamp(0.4rem, 1vw, 0.8rem); | |
| --title-size: clamp(1.3rem, 4.5vw, 2.2rem); | |
| --body-size: clamp(0.7rem, 1.1vw, 0.9rem); | |
| } | |
| .nav-dots, .keyboard-hint { display: none; } | |
| } | |
| @media (max-height: 500px) { | |
| :root { | |
| --slide-padding: clamp(0.5rem, 2.5vw, 1.5rem); | |
| --title-size: clamp(1.1rem, 4vw, 1.8rem); | |
| --h2-size: clamp(0.95rem, 2.5vw, 1.4rem); | |
| --body-size: clamp(0.65rem, 1vw, 0.8rem); | |
| } | |
| } | |
| @media (max-width: 600px) { | |
| :root { | |
| --title-size: clamp(1.4rem, 8vw, 2.5rem); | |
| } | |
| } | |
| @media (prefers-reduced-motion: reduce) { | |
| *, *::before, *::after { | |
| animation-duration: 0.01ms !important; | |
| transition-duration: 0.2s !important; | |
| } | |
| html { scroll-behavior: auto; } | |
| } | |
| /* =========================================== | |
| PRINT / PDF STYLES | |
| Forces landscape pages, one slide per page, | |
| preserves dark theme with background colors | |
| =========================================== */ | |
| @page { | |
| size: 13.333in 7.5in; /* 16:9 widescreen — matches PowerPoint */ | |
| margin: 0; | |
| } | |
| @media print { | |
| html, body { | |
| height: auto; | |
| overflow: visible; | |
| -webkit-print-color-adjust: exact !important; | |
| print-color-adjust: exact !important; | |
| color-adjust: exact !important; | |
| } | |
| html { | |
| scroll-snap-type: none; | |
| } | |
| .slide { | |
| width: 100vw; | |
| height: 100vh; | |
| overflow: hidden; | |
| page-break-after: always; | |
| break-after: page; | |
| page-break-inside: avoid; | |
| break-inside: avoid; | |
| scroll-snap-align: unset; | |
| } | |
| .slide:last-child { | |
| page-break-after: auto; | |
| break-after: auto; | |
| } | |
| /* Show all reveal elements immediately in print */ | |
| .reveal, .reveal-left, .reveal-right, .reveal-scale { | |
| opacity: 1 !important; | |
| transform: none !important; | |
| transition: none !important; | |
| } | |
| /* Hide interactive UI chrome */ | |
| .progress-bar, | |
| .nav-dots, | |
| .slide-counter, | |
| .keyboard-hint { | |
| display: none !important; | |
| } | |
| /* Ensure backgrounds print */ | |
| .slide, .card, .code-block, .flow-step, .table-wrap, | |
| .label, .badge, .v-flow-num, th, td, body { | |
| -webkit-print-color-adjust: exact !important; | |
| print-color-adjust: exact !important; | |
| } | |
| /* Grid backgrounds via ::before and ::after */ | |
| .grid-bg::after { | |
| -webkit-print-color-adjust: exact !important; | |
| print-color-adjust: exact !important; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Progress Bar --> | |
| <div class="progress-bar" id="progressBar"></div> | |
| <!-- Navigation Dots --> | |
| <nav class="nav-dots" id="navDots"></nav> | |
| <!-- Slide Counter --> | |
| <div class="slide-counter"> | |
| <span class="current" id="currentSlide">01</span> | |
| <span>/</span> | |
| <span id="totalSlides">15</span> | |
| </div> | |
| <!-- Keyboard Hint --> | |
| <div class="keyboard-hint"> | |
| <span class="key">↓</span> <span class="key">↑</span> navigate | |
| </div> | |
| <!-- ========================================== | |
| SLIDE 1: TITLE | |
| ========================================== --> | |
| <section class="slide title-slide grid-bg"> | |
| <div class="slide-content"> | |
| <span class="label reveal">Architecture Plan</span> | |
| <h1 class="reveal">Agent Action<br><span class="accent">Execution Layer</span></h1> | |
| <div class="title-accent reveal"></div> | |
| <p class="subtitle reveal">Closing the gap between classification and execution in the agent-service pipeline</p> | |
| <div class="meta reveal"> | |
| <span>Marcos Kuchak</span> | |
| <span>March 2026</span> | |
| <span>Draft</span> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 2: THE PROBLEM | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <span class="label red-label reveal">The Problem</span> | |
| <h2 class="reveal">The pipeline classifies actions<br>but <span class="red">never executes them</span></h2> | |
| <div class="grid-2 reveal" style="margin-top: var(--content-gap);"> | |
| <div class="card"> | |
| <h3 style="color: var(--red);">Auto-Execute Path</h3> | |
| <p>The <code>auto_executed</code> label logs "success" in the action log — <strong>no MCP tool is called</strong>. No side effect occurs.</p> | |
| </div> | |
| <div class="card"> | |
| <h3 style="color: var(--red);">Inbox Approval Path</h3> | |
| <p>User approves an inbox item — <strong>PATCH updates status in MongoDB</strong>. Nothing happens. No tool executes.</p> | |
| </div> | |
| </div> | |
| <div class="code-block reveal" style="margin-top: var(--content-gap);">Handler returns ProposedAction[] | |
| <span class="comment">→ enforceGate() classifies each action</span> | |
| <span class="comment">→ Tier 1 (safe): logs "success" → NOTHING EXECUTES</span> | |
| <span class="comment">→ Tier 2 (medium): logs "success" → NOTHING EXECUTES</span> | |
| <span class="comment">→ Tier 3 (risky): logs "pending_review" → NOTHING EXECUTES</span> | |
| User approves inbox item | |
| <span class="comment">→ PATCH /v1/inbox/:id → status updated → NOTHING EXECUTES</span></div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 3: THREE LAYERS OVERVIEW | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <span class="label reveal">Solution</span> | |
| <h2 class="reveal">Three layers to close the gap</h2> | |
| <div class="grid-3 reveal" style="margin-top: var(--content-gap);"> | |
| <div class="card" style="border-top: 3px solid var(--accent);"> | |
| <div style="display:flex; justify-content:space-between; align-items:center;"> | |
| <h3>Layer 1</h3> | |
| <span class="badge badge-accent">~80%</span> | |
| </div> | |
| <p style="margin-top:0.5em;"><strong class="white">Wrapped Tools</strong></p> | |
| <p style="margin-top:0.3em;">Every mutation MCP tool wrapped with inline confidence gate. Agent calls tool with <code>confidence</code> param. Wrapper decides: execute or defer.</p> | |
| </div> | |
| <div class="card" style="border-top: 3px solid var(--blue);"> | |
| <div style="display:flex; justify-content:space-between; align-items:center;"> | |
| <h3 style="color: var(--blue);">Layer 2</h3> | |
| <span class="badge badge-blue">~20%</span> | |
| </div> | |
| <p style="margin-top:0.5em;"><strong class="white">Post-Handler Gate</strong></p> | |
| <p style="margin-top:0.3em;">Existing flow, unchanged. For content-only outputs (Morning Brief, reports) that aren't tool calls. ProposedAction[] → gate → inbox.</p> | |
| </div> | |
| <div class="card" style="border-top: 3px solid var(--purple);"> | |
| <div style="display:flex; justify-content:space-between; align-items:center;"> | |
| <h3 style="color: var(--purple);">Layer 3</h3> | |
| <span class="badge badge-accent">Deferred</span> | |
| </div> | |
| <p style="margin-top:0.5em;"><strong class="white">Executor on Approval</strong></p> | |
| <p style="margin-top:0.3em;">When Layer 1 or 2 defers to inbox and user approves, the executor calls the corresponding MCP tool with stored payload.</p> | |
| </div> | |
| </div> | |
| <p class="reveal" style="margin-top: var(--content-gap); color: var(--text-muted); font-style: italic; font-size: var(--small-size);"> | |
| Key insight: the agent calls tools. The tools themselves are smart enough to gate their own execution. | |
| </p> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 4: LAYER 1 — CONCEPT | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <div class="layer-indicator reveal"> | |
| <div class="layer-dot active"></div> | |
| <div class="layer-dot"></div> | |
| <div class="layer-dot"></div> | |
| </div> | |
| <h2 class="reveal">Layer 1: <span class="accent">Wrapped Tools</span></h2> | |
| <p class="reveal" style="max-width: 750px;">Every mutation MCP tool is wrapped before being given to the agent. The wrapper adds <code>confidence</code> and <code>reasoning</code> to the tool's input schema. The agent calls the tool naturally — the wrapper gates execution deterministically.</p> | |
| <div class="split reveal" style="margin-top: var(--content-gap);"> | |
| <div> | |
| <h3 style="font-size: var(--body-size); color: var(--text-primary); margin-bottom: 0.5em;">What the agent sees</h3> | |
| <div class="code-block"><span class="fn">create_task</span>({ | |
| <span class="string">loanApplicationId</span>: <span class="string">"loan-123"</span>, | |
| <span class="string">title</span>: <span class="string">"Upload W2"</span>, | |
| <span class="accent">confidence</span>: <span class="type">0.92</span>, | |
| <span class="accent">reasoning</span>: <span class="string">"Condition requires W2"</span> | |
| })</div> | |
| </div> | |
| <div> | |
| <h3 style="font-size: var(--body-size); color: var(--text-primary); margin-bottom: 0.5em;">Per-tool, per-workflow config</h3> | |
| <ul class="bullet-list"> | |
| <li>R2 wraps <code>create_task</code> with threshold <strong class="green">0</strong> (always execute)</li> | |
| <li>R1 wraps <code>send_email</code> with threshold <strong class="amber">0.95</strong> (high bar)</li> | |
| <li>P4 wraps <code>send_email</code> with threshold <strong class="blue">0.85</strong> (routine follow-ups)</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 5: LAYER 1 — DECISION FLOW | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <div class="layer-indicator reveal"> | |
| <div class="layer-dot active"></div> | |
| <div class="layer-dot"></div> | |
| <div class="layer-dot"></div> | |
| </div> | |
| <h2 class="reveal">Wrapper Decision Flow</h2> | |
| <div class="v-flow reveal" style="margin-top: var(--content-gap);"> | |
| <div class="v-flow-step"> | |
| <div class="v-flow-num">1</div> | |
| <div class="v-flow-content"> | |
| <h3><span class="red">Compliance Blocked?</span></h3> | |
| <p>If action is in the NEVER_AUTO_EXECUTE set → write inbox item, return <code>"pending_approval"</code>. Agent adapts messaging.</p> | |
| </div> | |
| </div> | |
| <div class="v-flow-step"> | |
| <div class="v-flow-num">2</div> | |
| <div class="v-flow-content"> | |
| <h3><span class="green">Confidence ≥ Threshold & autoExecute?</span></h3> | |
| <p>Call real MCP tool → log success → return tool result to agent. Optionally create FYI inbox item.</p> | |
| </div> | |
| </div> | |
| <div class="v-flow-step"> | |
| <div class="v-flow-num">3</div> | |
| <div class="v-flow-content"> | |
| <h3><span class="amber">Fallback Strategy</span></h3> | |
| <p><strong>inbox_review</strong> → pending item, urgency high | <strong>draft</strong> → draft item, urgency medium | <strong>skip</strong> → log skipped, no item</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="grid-2 reveal" style="margin-top: var(--content-gap);"> | |
| <div class="card" style="border-left: 3px solid var(--green);"> | |
| <p style="font-size: var(--small-size);"><strong class="green">When executed</strong></p> | |
| <p style="font-size: var(--small-size); margin-top:0.3em; color:var(--text-muted);">Agent: "I created a task for uploading the W2."</p> | |
| </div> | |
| <div class="card" style="border-left: 3px solid var(--amber);"> | |
| <p style="font-size: var(--small-size);"><strong class="amber">When deferred</strong></p> | |
| <p style="font-size: var(--small-size); margin-top:0.3em; color:var(--text-muted);">Agent: "I've drafted the email but it needs your approval."</p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 6: LAYER 2 — POST-HANDLER GATE | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <div class="layer-indicator reveal"> | |
| <div class="layer-dot"></div> | |
| <div class="layer-dot active"></div> | |
| <div class="layer-dot"></div> | |
| </div> | |
| <h2 class="reveal">Layer 2: <span class="blue">Post-Handler Gate</span></h2> | |
| <p class="reveal" style="max-width:700px;">For content-only outputs that aren't tool calls. The existing <code>ProposedAction[]</code> → <code>enforceGate()</code> → inbox flow. <strong>Unchanged.</strong></p> | |
| <div class="grid-2 reveal" style="margin-top: var(--content-gap);"> | |
| <div> | |
| <div class="table-wrap"> | |
| <table> | |
| <thead><tr><th>Workflow</th><th>Output</th><th>Why not Layer 1</th></tr></thead> | |
| <tbody> | |
| <tr> | |
| <td><strong class="white">P1</strong> Morning Brief</td> | |
| <td>Risk summary</td> | |
| <td class="muted">The brief IS the content</td> | |
| </tr> | |
| <tr> | |
| <td><strong class="white">P2</strong> At-Risk Detection</td> | |
| <td>Risk report</td> | |
| <td class="muted">Content delivery, no mutation</td> | |
| </tr> | |
| <tr> | |
| <td><strong class="white">Future</strong></td> | |
| <td>Aggregated reports</td> | |
| <td class="muted">Post-execution summaries</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <div class="card" style="border-left: 3px solid var(--blue);"> | |
| <h3 style="color: var(--blue); font-size: var(--body-size);">Mixed Mode</h3> | |
| <p style="margin-top: 0.4em;">Workflows can use <strong>both</strong> layers:</p> | |
| <ul class="bullet-list" style="margin-top: 0.5em;"> | |
| <li>Wrapped tools for mutations <span class="muted">(Layer 1)</span></li> | |
| <li>ProposedActions for content <span class="muted">(Layer 2)</span></li> | |
| </ul> | |
| <p style="margin-top: 0.6em; font-size: var(--small-size); color: var(--text-muted);">Uses global <code>confidence.ts</code> config</p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 7: LAYER 3 — EXECUTOR ON APPROVAL | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <div class="layer-indicator reveal"> | |
| <div class="layer-dot"></div> | |
| <div class="layer-dot"></div> | |
| <div class="layer-dot active"></div> | |
| </div> | |
| <h2 class="reveal">Layer 3: <span class="purple">Executor on Approval</span></h2> | |
| <p class="reveal" style="max-width:700px;">When Layer 1 or 2 defers an action and the user approves, the executor dispatches the real MCP tool call.</p> | |
| <div class="split reveal" style="margin-top: var(--content-gap);"> | |
| <div> | |
| <div class="flow" style="flex-direction: column; align-items: stretch; gap: var(--element-gap);"> | |
| <div class="flow-step" style="white-space:normal;">User approves inbox item</div> | |
| <div style="text-align:center; color: var(--text-muted);">↓</div> | |
| <div class="flow-step active" style="white-space:normal;">PATCH /v1/inbox/:id → Executor reads payload + action type</div> | |
| <div style="text-align:center; color: var(--text-muted);">↓</div> | |
| <div class="flow-step" style="white-space:normal;">Calls MCP tool via ephemeral client</div> | |
| <div style="text-align:center; color: var(--text-muted);">↓</div> | |
| <div class="flow-step" style="white-space:normal;">Updates action_log + inbox with result</div> | |
| </div> | |
| </div> | |
| <div> | |
| <h3 style="font-size: var(--body-size); color: var(--text-primary); margin-bottom: 0.6em;">MCP Connection</h3> | |
| <p style="margin-bottom: var(--element-gap);">The executor creates an ephemeral <code>McpClientManager</code> scoped to the tenant, shares the global tool metadata cache, and cleans up in a <code>finally</code> block.</p> | |
| <h3 style="font-size: var(--body-size); color: var(--text-primary); margin-bottom: 0.4em; margin-top: 0.6em;">Error Handling</h3> | |
| <p>If execution fails, inbox item is still marked approved. <code>executionStatus: "failed"</code> signals frontend to show retry.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 8: STALE PAYLOAD STRATEGIES | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <span class="label amber-label reveal">Layer 3 Detail</span> | |
| <h2 class="reveal">Stale Payload <span class="amber">Strategies</span></h2> | |
| <p class="reveal" style="max-width:700px;">Payload is frozen at proposal time. User may approve hours later. Each action type declares its execution strategy:</p> | |
| <div class="grid-3 reveal" style="margin-top: var(--content-gap);"> | |
| <div class="card" style="border-top: 3px solid var(--green);"> | |
| <h3 style="color: var(--green);">Direct</h3> | |
| <p style="margin-top:0.3em;">Execute with stored payload as-is. For additive actions where staleness doesn't matter.</p> | |
| <p style="margin-top:0.5em; font-size: var(--small-size); color: var(--text-muted);">CREATE_TASK, CREATE_COMMENT, SEND_EMAIL, DRAFT_EMAIL, FLAG_TASK</p> | |
| </div> | |
| <div class="card" style="border-top: 3px solid var(--amber);"> | |
| <h3 style="color: var(--amber);">Pre-Check</h3> | |
| <p style="margin-top:0.3em;">Fetch current state via MCP, compare against payload, reject if data changed since proposal.</p> | |
| <p style="margin-top:0.5em; font-size: var(--small-size); color: var(--text-muted);">UPDATE_LOAN_DATA, UPDATE_FILE_PROGRESS, UPDATE_TASK_COMPLETE</p> | |
| </div> | |
| <div class="card" style="border-top: 3px solid var(--text-muted);"> | |
| <h3>None</h3> | |
| <p style="margin-top:0.3em;">No execution needed. The inbox item IS the content delivery.</p> | |
| <p style="margin-top:0.5em; font-size: var(--small-size); color: var(--text-muted);">MORNING_BRIEF</p> | |
| </div> | |
| </div> | |
| <div class="card reveal" style="margin-top: var(--content-gap); border-left: 3px solid var(--amber); background: rgba(255,179,71,0.04);"> | |
| <p style="font-size: var(--small-size);"><strong class="amber">Pre-check fail example:</strong> Agent proposes <code>closingDate: "April 15"</code> at 7 AM. LO emails new date at 10 AM. User approves at 1 PM → executor fetches current state → sees field changed → rejects with reason. No data regression.</p> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 9: TIER MAPPING — TIER 1 | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <span class="label green-label reveal">Tier Mapping</span> | |
| <h2 class="reveal">Tier 1: <span class="green">Always Auto-Execute</span></h2> | |
| <p class="reveal" style="max-width: 600px; margin-bottom: var(--content-gap);">Low-risk additive actions. Wrapped with threshold 0 by default — always execute unless a workflow overrides.</p> | |
| <div class="table-wrap reveal"> | |
| <table> | |
| <thead> | |
| <tr><th>Action</th><th>Threshold</th><th>Auto</th><th>Fallback</th><th>Rationale</th></tr> | |
| </thead> | |
| <tbody> | |
| <tr><td><strong class="white">CREATE_TASK</strong></td><td><span class="green">0</span></td><td><span class="badge badge-green">Yes</span></td><td>skip</td><td>Additive, easy to modify/delete</td></tr> | |
| <tr><td><strong class="white">CREATE_COMMENT</strong></td><td><span class="green">0</span></td><td><span class="badge badge-green">Yes</span></td><td>skip</td><td>Informational, no data mutation</td></tr> | |
| <tr><td><strong class="white">UPDATE_TASK_REVIEW</strong></td><td><span class="green">0</span></td><td><span class="badge badge-green">Yes</span></td><td>skip</td><td>State transition tracking</td></tr> | |
| <tr><td><strong class="white">FLAG_TASK</strong></td><td><span class="green">0</span></td><td><span class="badge badge-green">Yes</span></td><td>skip</td><td>Attention flag, easily reversed</td></tr> | |
| <tr><td><strong class="white">DRAFT_EMAIL</strong></td><td><span class="green">0</span></td><td><span class="badge badge-green">Yes</span></td><td>skip</td><td>Creates draft, doesn't send</td></tr> | |
| <tr><td><strong class="white">DRAFT_LO_UPDATE</strong></td><td><span class="green">0</span></td><td><span class="badge badge-green">Yes</span></td><td>skip</td><td>Creates draft for review</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 10: TIER MAPPING — TIER 2 & 3 | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <h2 class="reveal">Tier 2 <span class="amber">&</span> Tier 3</h2> | |
| <div class="grid-2 reveal" style="margin-top: var(--content-gap);"> | |
| <div> | |
| <h3 style="color: var(--amber); margin-bottom: 0.6em;">Tier 2: High Confidence Required</h3> | |
| <div class="table-wrap"> | |
| <table> | |
| <thead><tr><th>Action</th><th>Threshold</th><th>Fallback</th></tr></thead> | |
| <tbody> | |
| <tr><td><strong class="white">UPDATE_TASK_COMPLETE</strong></td><td><span class="amber">0.95</span></td><td>inbox_review</td></tr> | |
| <tr><td><strong class="white">SEND_EMAIL</strong></td><td><span class="amber">0.95</span></td><td>draft</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="card" style="margin-top: var(--element-gap); border-left: 2px solid var(--amber);"> | |
| <p style="font-size: var(--small-size);"><strong class="amber">Example:</strong> Agent calls <code>send_email</code> with confidence 0.97 → executes. With 0.82 → defers to inbox as draft.</p> | |
| </div> | |
| </div> | |
| <div> | |
| <h3 style="color: var(--red); margin-bottom: 0.6em;">Tier 3: Always Require Approval</h3> | |
| <div class="table-wrap"> | |
| <table> | |
| <thead><tr><th>Action</th><th>Threshold</th><th>Fallback</th></tr></thead> | |
| <tbody> | |
| <tr><td><strong class="white">UPDATE_FILE_PROGRESS</strong></td><td><span class="red">1.0</span></td><td>inbox_review</td></tr> | |
| <tr><td><strong class="white">UPDATE_LOAN_DATA</strong></td><td><span class="red">1.0</span></td><td>inbox_review</td></tr> | |
| <tr><td><strong class="white">SEND_LO_UPDATE</strong></td><td><span class="red">1.0</span></td><td>inbox_review</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="card" style="margin-top: var(--element-gap); border-left: 2px solid var(--red);"> | |
| <p style="font-size: var(--small-size);"><span class="red"><strong>Compliance blocked.</strong></span> Wrapper NEVER executes, regardless of confidence. Execution only via Layer 3.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 11: wrapMcpTool API | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <span class="label reveal">API Design</span> | |
| <h2 class="reveal">The <code>wrapMcpTool</code> Helper</h2> | |
| <div class="split reveal" style="margin-top: var(--content-gap);"> | |
| <div class="code-block"><span class="keyword">type</span> <span class="type">WrapToolConfig</span> = { | |
| <span class="string">tool</span>: ToolDef; | |
| <span class="string">actionType</span>: <span class="type">string</span>; <span class="comment">// "CREATE_TASK"</span> | |
| <span class="string">threshold</span>: <span class="type">number</span>; <span class="comment">// per-tool, per-workflow</span> | |
| <span class="string">autoExecute</span>: <span class="type">boolean</span>; | |
| <span class="string">fallback</span>: <span class="string">"draft"</span> | <span class="string">"inbox_review"</span> | <span class="string">"skip"</span>; | |
| <span class="string">complianceBlocked</span>?: <span class="type">boolean</span>; | |
| <span class="string">actionLogRepo</span>: ActionLogRepo; | |
| <span class="string">inboxRepo</span>: InboxRepo; | |
| <span class="string">tenantId</span>: <span class="type">string</span>; | |
| <span class="string">workflowId</span>: <span class="type">string</span>; | |
| <span class="string">loanApplicationId</span>?: <span class="type">string</span>; | |
| <span class="string">targetUserId</span>?: <span class="type">string</span>; | |
| }; | |
| <span class="keyword">function</span> <span class="fn">wrapMcpTool</span>(config): <span class="type">ToolDef</span>;</div> | |
| <div> | |
| <ul class="bullet-list"> | |
| <li><strong class="white">Centralized mechanism</strong> — every workflow uses the same helper</li> | |
| <li><strong class="white">Gate logic in one place</strong> — compliance → threshold → fallback</li> | |
| <li><strong class="white">Workflows only configure</strong> WHICH tools and WHAT thresholds</li> | |
| <li><strong class="white">Schema extension</strong> — adds <code>confidence</code> + <code>reasoning</code> to input schema automatically</li> | |
| <li><strong class="white">Strips extra params</strong> before passing to real MCP tool</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 12: INBOX LIFECYCLE | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <span class="label blue-label reveal">Inbox</span> | |
| <h2 class="reveal">Inbox Item Lifecycle</h2> | |
| <div class="grid-3 reveal" style="margin-top: var(--content-gap);"> | |
| <div class="card" style="border-top: 3px solid var(--accent);"> | |
| <h3 style="font-size: var(--body-size); color: var(--accent);">Informational</h3> | |
| <p style="font-size: var(--small-size); margin-top: 0.3em; color: var(--text-muted);">"Agent did this"</p> | |
| <p style="font-size: var(--small-size); margin-top: 0.5em;">Created by Layer 1 wrapper on auto-execute (optional FYI)</p> | |
| <div class="flow" style="margin-top: 0.6em;"> | |
| <span class="flow-step" style="font-size: clamp(0.55rem, 0.7vw, 0.7rem);">pending</span> | |
| <span class="flow-arrow">→</span> | |
| <span class="flow-step" style="font-size: clamp(0.55rem, 0.7vw, 0.7rem);">reviewed / dismissed</span> | |
| </div> | |
| </div> | |
| <div class="card" style="border-top: 3px solid var(--amber);"> | |
| <h3 style="font-size: var(--body-size); color: var(--amber);">Decisional</h3> | |
| <p style="font-size: var(--small-size); margin-top: 0.3em; color: var(--text-muted);">"Approve this?"</p> | |
| <p style="font-size: var(--small-size); margin-top: 0.5em;">Created by Layer 1 (deferred) or Layer 2 gate</p> | |
| <div class="flow" style="margin-top: 0.6em;"> | |
| <span class="flow-step" style="font-size: clamp(0.55rem, 0.7vw, 0.7rem);">pending</span> | |
| <span class="flow-arrow">→</span> | |
| <span class="flow-step active" style="font-size: clamp(0.55rem, 0.7vw, 0.7rem);">approved → executor</span> | |
| </div> | |
| </div> | |
| <div class="card" style="border-top: 3px solid var(--blue);"> | |
| <h3 style="font-size: var(--body-size); color: var(--blue);">Content</h3> | |
| <p style="font-size: var(--small-size); margin-top: 0.3em; color: var(--text-muted);">"Here's your brief"</p> | |
| <p style="font-size: var(--small-size); margin-top: 0.5em;">Created by Layer 2 gate (content delivery)</p> | |
| <div class="flow" style="margin-top: 0.6em;"> | |
| <span class="flow-step" style="font-size: clamp(0.55rem, 0.7vw, 0.7rem);">pending</span> | |
| <span class="flow-arrow">→</span> | |
| <span class="flow-step" style="font-size: clamp(0.55rem, 0.7vw, 0.7rem);">reviewed / dismissed</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="table-wrap reveal" style="margin-top: var(--content-gap);"> | |
| <table> | |
| <thead><tr><th>Source</th><th>executionStatus</th><th>UI Behavior</th></tr></thead> | |
| <tbody> | |
| <tr><td>Wrapper auto-executed (FYI)</td><td><span class="badge badge-green">executed</span></td><td>Show as done, dismiss option</td></tr> | |
| <tr><td>Wrapper deferred</td><td><span class="badge badge-amber">pending_approval</span></td><td>Show approve/reject buttons</td></tr> | |
| <tr><td>Layer 2 content</td><td><span class="badge badge-blue">not_applicable</span></td><td>Show review/dismiss</td></tr> | |
| <tr><td>Approved + executor ran</td><td><span class="badge badge-green">executed</span></td><td>Show as completed</td></tr> | |
| <tr><td>Approved + executor failed</td><td><span class="badge badge-red">failed</span></td><td>Show retry option</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 13: PER-WORKFLOW IMPACT | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <span class="label purple-label reveal">Impact</span> | |
| <h2 class="reveal">Per-Workflow Impact</h2> | |
| <div class="table-wrap reveal" style="margin-top: var(--content-gap);"> | |
| <table> | |
| <thead><tr><th>Workflow</th><th>Layer 1 (Wrapped Tools)</th><th>Layer 2 (Content)</th><th>Status</th></tr></thead> | |
| <tbody> | |
| <tr><td><strong class="white">P1</strong> Morning Brief</td><td class="muted">— (reads only)</td><td>Risk summary per processor</td><td><span class="badge badge-green">No change</span></td></tr> | |
| <tr><td><strong class="white">P5</strong> Tasks Aging</td><td><code>flag_task</code>, <code>create_comment</code></td><td class="muted">—</td><td><span class="badge badge-amber">Update</span></td></tr> | |
| <tr><td><strong class="white">R2</strong> Cond. Approval</td><td><code>create_task</code>, <code>update_loan</code></td><td class="muted">—</td><td><span class="badge badge-amber">In progress</span></td></tr> | |
| <tr><td><strong class="white">P3</strong> LO Status</td><td><code>create_lo_update</code>, <code>send_lo_update</code></td><td class="muted">—</td><td><span class="badge badge-amber">In progress</span></td></tr> | |
| <tr><td><strong class="white">R1</strong> Draft Email</td><td><code>draft_email</code>, <code>send_email</code> (0.95)</td><td class="muted">—</td><td><span class="badge badge-blue">Future</span></td></tr> | |
| <tr><td><strong class="white">R3</strong> Doc Matching</td><td><code>create_comment</code>, <code>update_task</code> (0.95)</td><td class="muted">—</td><td><span class="badge badge-blue">Future</span></td></tr> | |
| <tr><td><strong class="white">P2</strong> At-Risk</td><td><code>flag_task</code>, <code>create_comment</code></td><td>Risk report summary</td><td><span class="badge badge-blue">Future</span></td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 14: IMPLEMENTATION PHASES | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <span class="label reveal">Roadmap</span> | |
| <h2 class="reveal">Implementation Phases</h2> | |
| <div class="grid-3 reveal" style="margin-top: var(--content-gap);"> | |
| <div class="card" style="border-top: 3px solid var(--accent);"> | |
| <div style="display:flex; justify-content:space-between; align-items:center;"> | |
| <h3>Phase 1</h3> | |
| <span class="badge badge-accent">Unblocks all</span> | |
| </div> | |
| <p style="margin-top:0.4em;"><strong class="white">Tool Wrapper</strong></p> | |
| <ul class="bullet-list" style="margin-top:0.4em;"> | |
| <li>Create <code>wrapMcpTool()</code> helper</li> | |
| <li>Schema extension logic</li> | |
| <li>Expose repos to handlers</li> | |
| <li>Unit tests (all gate paths)</li> | |
| <li>P5 proof-of-concept</li> | |
| </ul> | |
| </div> | |
| <div class="card" style="border-top: 3px solid var(--purple);"> | |
| <div style="display:flex; justify-content:space-between; align-items:center;"> | |
| <h3 style="color: var(--purple);">Phase 2</h3> | |
| <span class="badge badge-accent">Tier 2-3</span> | |
| </div> | |
| <p style="margin-top:0.4em;"><strong class="white">Executor on Approval</strong></p> | |
| <ul class="bullet-list" style="margin-top:0.4em;"> | |
| <li>Action executor + registry</li> | |
| <li>Ephemeral MCP client</li> | |
| <li>PATCH endpoint enhancement</li> | |
| <li><code>executionStatus</code> field</li> | |
| <li>Unit tests</li> | |
| </ul> | |
| </div> | |
| <div class="card" style="border-top: 3px solid var(--blue);"> | |
| <div style="display:flex; justify-content:space-between; align-items:center;"> | |
| <h3 style="color: var(--blue);">Phase 3</h3> | |
| <span class="badge badge-accent">Adoption</span> | |
| </div> | |
| <p style="margin-top:0.4em;"><strong class="white">Per-Workflow Rollout</strong></p> | |
| <ul class="bullet-list" style="margin-top:0.4em;"> | |
| <li>R2: Andre</li> | |
| <li>P3: Andres S.</li> | |
| <li>P5: Marcos/Matheus</li> | |
| <li>All new workflows adopt from day one</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 15: RISKS & MITIGATIONS | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <span class="label red-label reveal">Risks</span> | |
| <h2 class="reveal">Risks & Mitigations</h2> | |
| <div class="grid-2 reveal" style="margin-top: var(--content-gap);"> | |
| <div class="card"> | |
| <h3 style="color: var(--red); font-size: var(--body-size);">Agent games confidence (always 1.0)</h3> | |
| <p style="margin-top:0.3em; font-size: var(--small-size);">Prompt engineering + <code>complianceBlocked</code> always blocks regardless. Can add max confidence cap per wrapper.</p> | |
| </div> | |
| <div class="card"> | |
| <h3 style="color: var(--red); font-size: var(--body-size);">Wrapper executes but logging fails</h3> | |
| <p style="margin-top:0.3em; font-size: var(--small-size);">Try/catch — if log fails, log error but don't fail the tool. Better done than rolled back.</p> | |
| </div> | |
| <div class="card"> | |
| <h3 style="color: var(--red); font-size: var(--body-size);">Executor MCP connection fails on approval</h3> | |
| <p style="margin-top:0.3em; font-size: var(--small-size);">Return error in API response. Set <code>executionStatus: "failed"</code>. Frontend shows retry.</p> | |
| </div> | |
| <div class="card"> | |
| <h3 style="color: var(--amber); font-size: var(--body-size);">Per-workflow threshold drift</h3> | |
| <p style="margin-top:0.3em; font-size: var(--small-size);">This is a <strong class="green">feature</strong>, not a bug. Different contexts warrant different thresholds. Document conventions.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 16: FILES SUMMARY | |
| ========================================== --> | |
| <section class="slide grid-bg"> | |
| <div class="slide-content stagger"> | |
| <span class="label reveal">Scope</span> | |
| <h2 class="reveal">Files Summary</h2> | |
| <div class="grid-2 reveal" style="margin-top: var(--content-gap);"> | |
| <div> | |
| <h3 style="color: var(--green); font-size: var(--body-size); margin-bottom: 0.5em;">New Files</h3> | |
| <div class="table-wrap"> | |
| <table> | |
| <thead><tr><th>File</th><th>Purpose</th></tr></thead> | |
| <tbody> | |
| <tr><td><code>tool-wrapper.ts</code></td><td>wrapMcpTool() + gate logic</td></tr> | |
| <tr><td><code>tool-wrapper.spec.ts</code></td><td>Unit tests</td></tr> | |
| <tr><td><code>action-executor.ts</code></td><td>Registry + executor + MCP</td></tr> | |
| <tr><td><code>action-executor.spec.ts</code></td><td>Unit tests</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <div> | |
| <h3 style="color: var(--amber); font-size: var(--body-size); margin-bottom: 0.5em;">Modified Files</h3> | |
| <div class="table-wrap"> | |
| <table> | |
| <thead><tr><th>File</th><th>Change</th></tr></thead> | |
| <tbody> | |
| <tr><td><code>domain/types.ts</code></td><td>Repo access types</td></tr> | |
| <tr><td><code>orchestrator.ts</code></td><td>Pass repos to handlers</td></tr> | |
| <tr><td><code>inbox/update.ts</code></td><td>Executor on approval</td></tr> | |
| <tr><td><code>inbox-item.schema</code></td><td>executionStatus field</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="card reveal" style="margin-top: var(--content-gap); border-left: 3px solid var(--text-muted);"> | |
| <p style="font-size: var(--small-size);"><strong>Unchanged:</strong> <code>confidence.ts</code> (Layer 2 only), <code>compliance.ts</code>, <code>confidence-gate.ts</code>, <code>mcp-normalize.ts</code>, <code>mastra/adapter.ts</code></p> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ========================================== | |
| SLIDE 17: CLOSING | |
| ========================================== --> | |
| <section class="slide title-slide grid-bg"> | |
| <div class="slide-content" style="align-items: center; text-align: center;"> | |
| <h1 class="reveal" style="font-size: clamp(1.5rem, 4vw, 3.5rem);">The agent calls tools.<br><span class="accent">The tools gate themselves.</span></h1> | |
| <div class="title-accent reveal" style="margin: var(--content-gap) auto;"></div> | |
| <p class="reveal" style="color: var(--text-secondary); max-width: 600px; margin: 0 auto;"> | |
| Phase 1 unblocks all workflows. Phase 2 closes the approval loop. Phase 3 is per-team adoption. | |
| </p> | |
| <div class="meta reveal" style="justify-content: center;"> | |
| <span>Ready for review</span> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- =========================================== | |
| JAVASCRIPT: Slide Controller | |
| =========================================== --> | |
| <script> | |
| class SlidePresentation { | |
| constructor() { | |
| this.slides = document.querySelectorAll('.slide'); | |
| this.totalSlides = this.slides.length; | |
| this.currentIndex = 0; | |
| this.isTransitioning = false; | |
| document.getElementById('totalSlides').textContent = | |
| String(this.totalSlides).padStart(2, '0'); | |
| this.createNavDots(); | |
| this.setupObserver(); | |
| this.setupKeyboard(); | |
| this.setupWheel(); | |
| this.setupTouch(); | |
| this.updateProgress(); | |
| /* Make first slide visible immediately */ | |
| this.slides[0].classList.add('visible'); | |
| } | |
| /* --- Navigation Dots --- */ | |
| createNavDots() { | |
| const nav = document.getElementById('navDots'); | |
| this.slides.forEach((_, i) => { | |
| const dot = document.createElement('button'); | |
| dot.classList.add('nav-dot'); | |
| if (i === 0) dot.classList.add('active'); | |
| dot.setAttribute('aria-label', `Go to slide ${i + 1}`); | |
| dot.addEventListener('click', () => this.goToSlide(i)); | |
| nav.appendChild(dot); | |
| }); | |
| this.dots = nav.querySelectorAll('.nav-dot'); | |
| } | |
| /* --- Intersection Observer for scroll-triggered animations --- */ | |
| setupObserver() { | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('visible'); | |
| const index = Array.from(this.slides).indexOf(entry.target); | |
| if (index !== -1) { | |
| this.currentIndex = index; | |
| this.updateUI(); | |
| } | |
| } | |
| }); | |
| }, { threshold: 0.5 }); | |
| this.slides.forEach(slide => observer.observe(slide)); | |
| } | |
| /* --- Keyboard Navigation --- */ | |
| setupKeyboard() { | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'ArrowDown' || e.key === 'ArrowRight' || e.key === ' ') { | |
| e.preventDefault(); | |
| this.next(); | |
| } else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') { | |
| e.preventDefault(); | |
| this.prev(); | |
| } else if (e.key === 'Home') { | |
| e.preventDefault(); | |
| this.goToSlide(0); | |
| } else if (e.key === 'End') { | |
| e.preventDefault(); | |
| this.goToSlide(this.totalSlides - 1); | |
| } | |
| }); | |
| } | |
| /* --- Mouse Wheel with debounce --- */ | |
| setupWheel() { | |
| let lastWheelTime = 0; | |
| document.addEventListener('wheel', (e) => { | |
| const now = Date.now(); | |
| if (now - lastWheelTime < 800) return; | |
| lastWheelTime = now; | |
| if (e.deltaY > 30) this.next(); | |
| else if (e.deltaY < -30) this.prev(); | |
| }, { passive: true }); | |
| } | |
| /* --- Touch / Swipe --- */ | |
| setupTouch() { | |
| let startY = 0; | |
| document.addEventListener('touchstart', (e) => { | |
| startY = e.touches[0].clientY; | |
| }, { passive: true }); | |
| document.addEventListener('touchend', (e) => { | |
| const diff = startY - e.changedTouches[0].clientY; | |
| if (Math.abs(diff) > 60) { | |
| if (diff > 0) this.next(); | |
| else this.prev(); | |
| } | |
| }, { passive: true }); | |
| } | |
| /* --- Navigation Methods --- */ | |
| next() { | |
| if (this.currentIndex < this.totalSlides - 1) { | |
| this.goToSlide(this.currentIndex + 1); | |
| } | |
| } | |
| prev() { | |
| if (this.currentIndex > 0) { | |
| this.goToSlide(this.currentIndex - 1); | |
| } | |
| } | |
| goToSlide(index) { | |
| this.currentIndex = index; | |
| this.slides[index].scrollIntoView({ behavior: 'smooth' }); | |
| this.updateUI(); | |
| } | |
| /* --- UI Updates --- */ | |
| updateUI() { | |
| this.updateProgress(); | |
| this.updateDots(); | |
| this.updateCounter(); | |
| } | |
| updateProgress() { | |
| const progress = ((this.currentIndex + 1) / this.totalSlides) * 100; | |
| document.getElementById('progressBar').style.width = `${progress}%`; | |
| } | |
| updateDots() { | |
| this.dots.forEach((dot, i) => { | |
| dot.classList.toggle('active', i === this.currentIndex); | |
| }); | |
| } | |
| updateCounter() { | |
| document.getElementById('currentSlide').textContent = | |
| String(this.currentIndex + 1).padStart(2, '0'); | |
| } | |
| } | |
| /* Initialize on DOM ready */ | |
| document.addEventListener('DOMContentLoaded', () => { | |
| new SlidePresentation(); | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment