Buckets:
| import{s as $e,n as ge,o as be}from"../chunks/scheduler.4048030c.js";import{S as we,i as xe,e as Q,s as o,c as i,h as ye,a as I,d as a,b as n,f as fe,j as r,g as le,k as ce,l as ve,m as s,n as l,t as d,o as h,p as m}from"../chunks/index.5d0b9360.js";import{C as ke,H as u,E as Te}from"../chunks/MermaidChart.svelte_svelte_type_style_lang.99c2e6c8.js";import{Q as A}from"../chunks/Question.e487118a.js";function Ce(de){let p,E,G,M,f,L,c,N,$,he="Test your ability to wire hooks into real workflows.",U,g,W,b,Y,w,J,x,B,y,R,v,K,k,D,T,V,C,X,P,Z,_,ee,q,me="If you got 4-5 correct, you can wire hooks into a real observability and guardrail flow. If not, review the hands-on example, especially the payload normalization and blocking patterns.",te,H,ae,S,pe="<li>One FastAPI + Gradio process is enough for a lightweight live dashboard</li> <li>Hook handlers should fail fast, sanitize payloads, and avoid shell-injection patterns</li> <li>A shared receiver works across Claude Code, Codex, OpenCode, and Pi once you normalize the event shape</li>",se,O,oe,F,ue="You’ve finished Unit 5: Hooks. You now have five end-to-end surfaces for shaping how a code agent works: skills, MCP servers, plugins, subagents, and hooks. The bonus unit goes one level deeper and builds an agent from scratch, so you understand what every one of those surfaces is plugging into.",ne,z,ie,j,re;return f=new ke({props:{containerStyle:"float: right; margin-left: 10px; display: inline-flex; position: relative; z-index: 10;"}}),c=new u({props:{title:"Quiz 2: Hooks in Practice",local:"quiz-2-hooks-in-practice",headingTag:"h1"}}),g=new u({props:{title:"Question 1: How should the dashboard server be structured?",local:"question-1-how-should-the-dashboard-server-be-structured",headingTag:"h2"}}),b=new A({props:{choices:[{text:"The Gradio app and the FastAPI receiver must run as two separate processes on two separate ports",explain:"They can, but the hands-on example uses `gr.mount_gradio_app(api, ui)` to run them in one process. Simpler, cheaper, and easier to deploy on Spaces."},{text:"Mounting Gradio on a FastAPI app with `gr.mount_gradio_app` lets one server serve both the `/event` endpoint and the Gradio UI",explain:"Correct! The FastAPI route `/event` is defined before Gradio is mounted at `/`, so POSTs hit the receiver and everything else lands on the UI.",correct:!0},{text:"You can only receive hook events from Claude Code — Codex, OpenCode, and Pi cannot post JSON",explain:'False. All four platforms can POST JSON to an HTTP endpoint: Claude Code uses `type: "http"`, Codex pipes the stdin payload to `curl`, and OpenCode plugins and Pi extensions call `fetch()`.'},{text:"Hook events should be written to a file rather than served over HTTP",explain:"File-based logging works, but it's harder to visualize live. HTTP + a polling dashboard gives real-time feedback with minimal code."}]}}),w=new u({props:{title:"Question 2: How does the Gradio dashboard refresh?",local:"question-2-how-does-the-gradio-dashboard-refresh",headingTag:"h2"}}),x=new A({props:{choices:[{text:"The dashboard polls with `gr.Timer(1.0)` because browsers can't receive server-pushed events from Python",explain:"Browsers can receive SSE or websockets, but polling a shared in-memory `deque` is the simplest pattern for this kind of dashboard."},{text:"Polling with `gr.Timer` re-runs the refresh function every N seconds, which re-reads the shared `events` buffer and re-renders the table and chart",explain:"Correct! Timer is the idiomatic Gradio primitive for periodic refresh.",correct:!0},{text:"The `gr.Timer` must be nested inside `gr.Row` or it won't fire",explain:"False. Timers are invisible components; they can live anywhere in the Blocks context."},{text:"The timer is mandatory — Gradio dashboards can't update any other way",explain:"False. You could also emit events via `gr.State` + server-side streaming. Timer is just the simplest fit for this use case."}]}}),y=new u({props:{title:"Question 3: How do hook-based guardrails block dangerous actions?",local:"question-3-how-do-hook-based-guardrails-block-dangerous-actions",headingTag:"h2"}}),v=new A({props:{choices:[{text:"To block a dangerous Bash command in Claude Code, the hook returns HTTP 500",explain:'False. HTTP 500 is treated as a non-blocking error. To block, respond 200 with `hookSpecificOutput.permissionDecision = "deny"`, or use a command hook that exits 2 with a stderr reason.'},{text:"A command hook that exits 2 and prints a reason on stderr is the documented way to block a tool call in Claude Code and Codex",explain:"Correct! Both platforms use the same convention: exit 2 + stderr means block, and the stderr message surfaces to the agent.",correct:!0},{text:"In OpenCode, you block a tool by editing the user's prompt",explain:"False. In OpenCode you block by throwing an Error inside `tool.execute.before`."},{text:"Guardrails should live in the model's system prompt rather than in hooks",explain:"Prompt-based guardrails are useful but unreliable — the model can forget them. Hooks enforce them deterministically."}]}}),k=new u({props:{title:"Question 4: What makes shell-command hooks safe?",local:"question-4-what-makes-shell-command-hooks-safe",headingTag:"h2"}}),T=new A({props:{choices:[{text:"Piping `curl --max-time 2` inside a Codex command hook is pointless; the agent never waits for hooks",explain:"False. Command hooks run synchronously with the tool call. If the dashboard is slow and you don't set a timeout, the agent stalls. `--max-time` is a real protection."},{text:"Shell command hooks can become a shell-injection risk if you interpolate JSON payloads into the command string — piping via `jq -c` and `curl --data-binary @-` is safer",explain:"Correct! Shape the payload with `jq` and push it via stdin rather than splicing strings into the command line.",correct:!0},{text:"There's no need to worry about hook payloads containing secrets, since the agent's sandbox already redacts them",explain:"False. Hook payloads include `tool_input` verbatim — including commands and prompts that may contain secrets. Redact in the dashboard normalizer."},{text:"You must commit `.claude/settings.local.json` to share hooks with teammates",explain:"False. `.claude/settings.json` is the committed file; `.claude/settings.local.json` is gitignored and for personal overrides."}]}}),C=new u({props:{title:"Question 5: Why can one dashboard support multiple agents?",local:"question-5-why-can-one-dashboard-support-multiple-agents",headingTag:"h2"}}),P=new A({props:{choices:[{text:"The same dashboard works for Claude Code, Codex, OpenCode, and Pi because the server normalizes four different payload shapes into one consistent record",explain:"Correct! The `_normalize` function maps each platform's field names onto `{platform, event, tool, args}` so the UI doesn't need platform-specific branches.",correct:!0},{text:"The dashboard only works for one platform at a time; you must redeploy it when switching agents",explain:"False. The normalizer accepts any of the four platforms simultaneously — they just arrive with different platform values from either the body or headers."},{text:"OpenCode events are incompatible with the same receiver because OpenCode plugins can't make HTTP calls",explain:"False. OpenCode plugins can and do call `fetch()` directly from the TS/JS runtime."},{text:"You have to run a separate dashboard per platform, then merge the logs afterwards",explain:"False. A single receiver handles all four because they can all POST JSON to the same URL."}]}}),_=new u({props:{title:"Summary",local:"summary",headingTag:"h2"}}),H=new u({props:{title:"Key Takeaways",local:"key-takeaways",headingTag:"h2"}}),O=new u({props:{title:"What’s Next",local:"whats-next",headingTag:"h2"}}),z=new Te({props:{source:"https://github.com/huggingface/context-course/blob/main/units/en/unit5/quiz2.mdx"}}),{c(){p=Q("meta"),E=o(),G=Q("p"),M=o(),i(f.$$.fragment),L=o(),i(c.$$.fragment),N=o(),$=Q("p"),$.textContent=he,U=o(),i(g.$$.fragment),W=o(),i(b.$$.fragment),Y=o(),i(w.$$.fragment),J=o(),i(x.$$.fragment),B=o(),i(y.$$.fragment),R=o(),i(v.$$.fragment),K=o(),i(k.$$.fragment),D=o(),i(T.$$.fragment),V=o(),i(C.$$.fragment),X=o(),i(P.$$.fragment),Z=o(),i(_.$$.fragment),ee=o(),q=Q("p"),q.textContent=me,te=o(),i(H.$$.fragment),ae=o(),S=Q("ul"),S.innerHTML=pe,se=o(),i(O.$$.fragment),oe=o(),F=Q("p"),F.textContent=ue,ne=o(),i(z.$$.fragment),ie=o(),j=Q("p"),this.h()},l(e){const t=ye("svelte-u9bgzb",document.head);p=I(t,"META",{name:!0,content:!0}),t.forEach(a),E=n(e),G=I(e,"P",{}),fe(G).forEach(a),M=n(e),r(f.$$.fragment,e),L=n(e),r(c.$$.fragment,e),N=n(e),$=I(e,"P",{"data-svelte-h":!0}),le($)!=="svelte-1yw8q7l"&&($.textContent=he),U=n(e),r(g.$$.fragment,e),W=n(e),r(b.$$.fragment,e),Y=n(e),r(w.$$.fragment,e),J=n(e),r(x.$$.fragment,e),B=n(e),r(y.$$.fragment,e),R=n(e),r(v.$$.fragment,e),K=n(e),r(k.$$.fragment,e),D=n(e),r(T.$$.fragment,e),V=n(e),r(C.$$.fragment,e),X=n(e),r(P.$$.fragment,e),Z=n(e),r(_.$$.fragment,e),ee=n(e),q=I(e,"P",{"data-svelte-h":!0}),le(q)!=="svelte-bfurtr"&&(q.textContent=me),te=n(e),r(H.$$.fragment,e),ae=n(e),S=I(e,"UL",{"data-svelte-h":!0}),le(S)!=="svelte-lgk09"&&(S.innerHTML=pe),se=n(e),r(O.$$.fragment,e),oe=n(e),F=I(e,"P",{"data-svelte-h":!0}),le(F)!=="svelte-1rc0fwd"&&(F.textContent=ue),ne=n(e),r(z.$$.fragment,e),ie=n(e),j=I(e,"P",{}),fe(j).forEach(a),this.h()},h(){ce(p,"name","hf:doc:metadata"),ce(p,"content",Pe)},m(e,t){ve(document.head,p),s(e,E,t),s(e,G,t),s(e,M,t),l(f,e,t),s(e,L,t),l(c,e,t),s(e,N,t),s(e,$,t),s(e,U,t),l(g,e,t),s(e,W,t),l(b,e,t),s(e,Y,t),l(w,e,t),s(e,J,t),l(x,e,t),s(e,B,t),l(y,e,t),s(e,R,t),l(v,e,t),s(e,K,t),l(k,e,t),s(e,D,t),l(T,e,t),s(e,V,t),l(C,e,t),s(e,X,t),l(P,e,t),s(e,Z,t),l(_,e,t),s(e,ee,t),s(e,q,t),s(e,te,t),l(H,e,t),s(e,ae,t),s(e,S,t),s(e,se,t),l(O,e,t),s(e,oe,t),s(e,F,t),s(e,ne,t),l(z,e,t),s(e,ie,t),s(e,j,t),re=!0},p:ge,i(e){re||(d(f.$$.fragment,e),d(c.$$.fragment,e),d(g.$$.fragment,e),d(b.$$.fragment,e),d(w.$$.fragment,e),d(x.$$.fragment,e),d(y.$$.fragment,e),d(v.$$.fragment,e),d(k.$$.fragment,e),d(T.$$.fragment,e),d(C.$$.fragment,e),d(P.$$.fragment,e),d(_.$$.fragment,e),d(H.$$.fragment,e),d(O.$$.fragment,e),d(z.$$.fragment,e),re=!0)},o(e){h(f.$$.fragment,e),h(c.$$.fragment,e),h(g.$$.fragment,e),h(b.$$.fragment,e),h(w.$$.fragment,e),h(x.$$.fragment,e),h(y.$$.fragment,e),h(v.$$.fragment,e),h(k.$$.fragment,e),h(T.$$.fragment,e),h(C.$$.fragment,e),h(P.$$.fragment,e),h(_.$$.fragment,e),h(H.$$.fragment,e),h(O.$$.fragment,e),h(z.$$.fragment,e),re=!1},d(e){e&&(a(E),a(G),a(M),a(L),a(N),a($),a(U),a(W),a(Y),a(J),a(B),a(R),a(K),a(D),a(V),a(X),a(Z),a(ee),a(q),a(te),a(ae),a(S),a(se),a(oe),a(F),a(ne),a(ie),a(j)),a(p),m(f,e),m(c,e),m(g,e),m(b,e),m(w,e),m(x,e),m(y,e),m(v,e),m(k,e),m(T,e),m(C,e),m(P,e),m(_,e),m(H,e),m(O,e),m(z,e)}}}const Pe='{"title":"Quiz 2: Hooks in Practice","local":"quiz-2-hooks-in-practice","sections":[{"title":"Question 1: How should the dashboard server be structured?","local":"question-1-how-should-the-dashboard-server-be-structured","sections":[],"depth":2},{"title":"Question 2: How does the Gradio dashboard refresh?","local":"question-2-how-does-the-gradio-dashboard-refresh","sections":[],"depth":2},{"title":"Question 3: How do hook-based guardrails block dangerous actions?","local":"question-3-how-do-hook-based-guardrails-block-dangerous-actions","sections":[],"depth":2},{"title":"Question 4: What makes shell-command hooks safe?","local":"question-4-what-makes-shell-command-hooks-safe","sections":[],"depth":2},{"title":"Question 5: Why can one dashboard support multiple agents?","local":"question-5-why-can-one-dashboard-support-multiple-agents","sections":[],"depth":2},{"title":"Summary","local":"summary","sections":[],"depth":2},{"title":"Key Takeaways","local":"key-takeaways","sections":[],"depth":2},{"title":"What’s Next","local":"whats-next","sections":[],"depth":2}],"depth":1}';function _e(de){return be(()=>{new URLSearchParams(window.location.search).get("fw")}),[]}class Fe extends we{constructor(p){super(),xe(this,p,_e,Ce,$e,{})}}export{Fe as component}; | |
Xet Storage Details
- Size:
- 12.1 kB
- Xet hash:
- d5aa6d71b8bf4b376e40b97be7e5e3d8e5e70382afdf18b469803ff714d11c5f
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.