BeforeTool-hook + kort-vy (strukturerade godkännanden) + release på master-push
Feature: strukturerade verktygsgodkännanden
- shared: protokoll för approval (daemon:approval, server:approval[-resolved],
web:approval-decision, server:approval-decision)
- daemon: hook-läge ('agent-helm hook' — samma binär), lokal 127.0.0.1-endpoint
som hooken POST:ar till, long-poll tills beslut; graceful default (allow) när
ingen UI; timeout-fallback; skickar AGENT_HELM_HOOK_PORT till agentens env
- server: routar daemon:approval -> prenumeranter, beslut -> daemon + döljer kort
- web: reaktiv approvals-lista + kort-overlay (Tillåt/Neka), mobilanpassad
CI:
- release.yaml triggar nu även på master-push -> rullande 'latest'-release
(versionerade releaser kvar på v*-taggar); install.sh hämtar 'latest'
Docs: hook-konfig (gemini settings.json) + hook-env i .env.example + DEPLOY.md.
Säkerhet: hook-endpoint binder endast 127.0.0.1; inga hemligheter i repot.
Verifierat lokalt: hook->daemon->server->kort->beslut->hook ger {"decision":"allow"};
alla paket typecheckar; web bygger.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -146,6 +146,15 @@ function handleDaemonMessage(sessionId: string, text: string): void {
|
||||
sendWeb(sub, { type: "server:exit", sessionId, code: msg.code });
|
||||
}
|
||||
broadcastSessions();
|
||||
} else if (msg.type === "daemon:approval") {
|
||||
const out: ServerToWeb = {
|
||||
type: "server:approval",
|
||||
sessionId,
|
||||
approvalId: msg.approvalId,
|
||||
toolName: msg.toolName,
|
||||
toolInput: msg.toolInput,
|
||||
};
|
||||
for (const sub of session.subscribers) sendWeb(sub, out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +175,29 @@ function handleWebMessage(ws: WebSocket, text: string): void {
|
||||
} else if (msg.type === "web:input") {
|
||||
const session = sessions.get(msg.sessionId);
|
||||
if (!session?.daemon || session.daemon.readyState !== WebSocket.OPEN) return;
|
||||
// Strukturerade hook-svar går INTE här — bara terminal-input (text/tangenter).
|
||||
// Terminal-input (text/tangenter) -> pty. Hook-svar går via web:approval-decision.
|
||||
session.daemon.send(JSON.stringify({ type: "server:input", data: msg.data }));
|
||||
} else if (msg.type === "web:approval-decision") {
|
||||
const session = sessions.get(msg.sessionId);
|
||||
if (!session) return;
|
||||
if (session.daemon && session.daemon.readyState === WebSocket.OPEN) {
|
||||
session.daemon.send(
|
||||
JSON.stringify({
|
||||
type: "server:approval-decision",
|
||||
approvalId: msg.approvalId,
|
||||
decision: msg.decision,
|
||||
reason: msg.reason,
|
||||
}),
|
||||
);
|
||||
}
|
||||
// Dölj kortet hos alla andra klienter som ser samma session.
|
||||
const resolved: ServerToWeb = {
|
||||
type: "server:approval-resolved",
|
||||
sessionId: msg.sessionId,
|
||||
approvalId: msg.approvalId,
|
||||
decision: msg.decision,
|
||||
};
|
||||
for (const sub of session.subscribers) sendWeb(sub, resolved);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user