Step through the last turn's edits, one diff at a time.

ADYA is a compact governance and product layer built around the existing registry surfaces, with a deterministic local mode and a DSH-native execution mode.
completed only when evidence exists, otherwise needs_review or a persisted failure state.ADYA_LLM_MODE=dsh execution through the checked-out DeepSeek Harness Node SDK: DSH owns the Agent loop, workflow, temporary child Agents, model-facing tools, and sessions; ADYA owns the product API, contracts, native capability policy, evidence projection, memory, and the HTTP/user/admin planes.The existing DSH tool registry, workflow engine, subagent lifecycle, and session persistence are reused. The ADYA native plugin adds one governed capability surface over the existing DSH registry instead of maintaining a second agent executor or an HTTP bridge.
$env:ADYA_ADMIN_TOKEN = "replace-with-a-long-random-local-token"
$env:ADYA_DATA_FILE = "data/state.json"
python server.py
Open http://localhost:8080. Send X-User-Id: alice when using the user plane. Send X-Admin-Token for admin operations.
The default mode is dependency-free and deterministic. It is explicitly labeled local-demo; its temporary graph agents are simulated ADYA demo agents, not proof of DSH-native execution.
$body = @{ goal = "Build a governed registry-backed multi-agent memory demo"; requested_agents = 5 } | ConvertTo-Json
Invoke-RestMethod http://localhost:8080/api/runs -Method Post -Headers @{ "X-User-Id" = "alice" } -ContentType "application/json" -Body $body
graph.nodes, events, governance, artifacts.review, memory_hits, and cleanup event.$feedback = @{ run_id = "<run-id-from-response>"; text = "Prefer registry evidence before synthesis"; score = 5 } | ConvertTo-Json
Invoke-RestMethod http://localhost:8080/api/feedback -Method Post -Headers @{ "X-User-Id" = "alice" } -ContentType "application/json" -Body $feedback
$body = @{ goal = "Use prior registry memory to build another governed demo" } | ConvertTo-Json
Invoke-RestMethod http://localhost:8080/api/runs -Method Post -Headers @{ "X-User-Id" = "alice" } -ContentType "application/json" -Body $body
ADYA_ADMIN_TOKEN and use the admin plane to inspect /api/admin/audit, /api/admin/errors, registries, contracts, and memory.Set ADYA_LLM_MODE=dsh and follow docs/setup.md. This path fails closed if the checked-out DSH runtime is unavailable; it does not fall back to the deterministic local orchestrator.
python -m unittest discover -s tests -v
python -m pytest -q
python -m py_compile harness.py server.py adya-share/harness.py adya-share/server.py
The architecture and configuration details are in docs/architecture.md and docs/setup.md. The native DSH acceptance test is conditional; run it with ADYA_DSH_ACCEPTANCE=1 when Node and the checked-out DSH source runtime are available. The checked-in fixture is keyless; a live provider run is a separate, unclaimed check.
The service is container-ready through Dockerfile, but no GCP deployment is claimed here. A GCP project, billing/free-tier choice, region, and authenticated gcloud account must be confirmed before an external deployment is performed. JSON state on local disk is suitable for a demo, not a multi-instance Cloud Run deployment without a durable datastore.
hooks/replay-theater.mjs 134 lines1// Replay Theater, from "Getting started with Claude Code mods"
2// (https://claude.dev/blog/getting-started-with-claude-code-mods/, Anthropic, 2026-10-01).
3// The post publishes the five hooks that record edits and register /replay
4// unchanged; the rest of the module (stepsFor, the diff, openReplay, the pane
5// and the hint) is completed to the post's description, marked where it starts.
6
7// Replay Theater: step through the last turn's edits, one diff at a time.
8
9// —— completed to the post's description (not in the published excerpt) ——
10
11const EDIT_TOOLS = new Set(["Edit", "Write"]);
12
13// Edits of the running turn, the sealed replay of the last one, and where the pane is.
14const state = { pending: [], replay: [], index: 0, open: false, placed: false };
15
16export function register(on) {
17 on("tool.call", async ($, e, next) => {
18 if (EDIT_TOOLS.has(e.tool)) state.pending.push(...(await stepsFor($, e))); // old/new text → diff
19 return next(e); // the edit runs untouched
20 });
21
22 on("turn.start", ($, e, next) => { if (!e.agentId) state.pending = []; return next(e); });
23
24 on("turn.complete", async ($, e, next) => {
25 const r = await next(e);
26 if (!e.agentId && state.pending.length) state.replay = state.pending; // one replay per turn
27 return r;
28 });
29
30 on("session.start", async ($, e, next) => {
31 const r = await next(e);
32 await $.command.register({ name: "replay", description: "Step through the last turn's file edits" });
33 return r;
34 });
35 on("command.run", { command: "replay" }, async ($, e) => ({ text: (await openReplay($)) ? "Replaying" : "No edits" }));
36
37 // —— completed to the post's description (not in the published excerpt) ——
38
39 on("ui.render", { component: "Pane" }, ($, e, next) => {
40 if (e.requestId !== "replay" || !state.open) return next(e);
41 return theater($, e);
42 });
43
44 on("ui.render", { component: "AbovePrompt" }, ($, e, next) => {
45 if (state.open && !state.placed) return theater($, e); // no pane could be placed: the same tree, inline
46 if (state.open || state.replay.length === 0) return next(e);
47 const { Box, Text, Button } = $.ui.resolve(e);
48 const n = state.replay.length;
49 return Box({
50 flexDirection: "row",
51 paddingX: 1,
52 gap: 1,
53 children: [
54 Text({ color: "magenta", bold: true, children: "Replay Theater" }),
55 Text({ dimColor: true, children: `${n} edit${n === 1 ? "" : "s"} this turn — press r or type /replay` }),
56 Button({ label: "Replay", hotkey: "r", onPress: () => openReplay($) }),
57 ],
58 });
59 });
60}
61
62// One replay step per edit: the file, and the text before and after.
63async function stepsFor($, e) {
64 const file = String(e.file_path ?? "");
65 if (e.tool === "Edit") {
66 return [{ file, before: String(e.old_string ?? ""), after: String(e.new_string ?? "") }];
67 }
68 // For a Write, the old contents are read just before the write lands, so the diff is real.
69 const before = await $.fs.read(file).catch(() => "");
70 return [{ file, before, after: String(e.content ?? "") }];
71}
72
73async function openReplay($) {
74 if (state.replay.length === 0) return false;
75 state.index = 0;
76 state.open = true;
77 const opened = await $.ui.open({ id: "replay", title: "Replay Theater", focus: true });
78 state.placed = opened.isPlaced;
79 $.ui.invalidate("ui.render");
80 return true;
81}
82
83function step(delta, $) {
84 state.index = Math.min(Math.max(state.index + delta, 0), state.replay.length - 1);
85 $.ui.invalidate("ui.render");
86}
87
88async function close($) {
89 state.open = false;
90 await $.ui.close({ id: "replay" });
91 $.ui.invalidate("ui.render");
92}
93
94// Lines the edit removed and added, as a unified diff shows them.
95function diffLines(before, after) {
96 const old = before.split("\n");
97 const neu = after.split("\n");
98 const common = new Set(neu);
99 const commonOld = new Set(old);
100 return [
101 ...old.filter((line) => !common.has(line)).map((line) => ({ sign: "-", line })),
102 ...neu.filter((line) => !commonOld.has(line)).map((line) => ({ sign: "+", line })),
103 ];
104}
105
106function theater($, e) {
107 const { Box, Text, Button } = $.ui.resolve(e);
108 const current = state.replay[state.index];
109 const strip = state.replay.map((_, i) => Text({ color: i === state.index ? "magenta" : undefined, bold: i === state.index, children: ` ${i + 1} ` }));
110 const diff = diffLines(current.before, current.after).map(({ sign, line }) =>
111 Text({ color: sign === "+" ? "green" : "red", children: `${sign} ${line}` }),
112 );
113 return Box({
114 flexDirection: "column",
115 border: true,
116 borderColor: "magenta",
117 paddingX: 1,
118 children: [
119 Box({ flexDirection: "row", children: [Text({ bold: true, children: "Replay Theater " }), ...strip] }),
120 Text({ dimColor: true, children: `${current.file} (step ${state.index + 1} of ${state.replay.length})` }),
121 ...diff,
122 Box({
123 flexDirection: "row",
124 gap: 2,
125 children: [
126 Button({ label: "Prev", hotkey: "p", disabled: state.index === 0, onPress: () => step(-1, $) }),
127 Button({ label: "Next", hotkey: "n", disabled: state.index >= state.replay.length - 1, onPress: () => step(1, $) }),
128 Button({ label: "Close", hotkey: "q", onPress: () => close($) }),
129 ],
130 }),
131 ],
132 });
133}
134