{"slug": "twokeys-ai-agents-can-act-but-should-they-decide", "title": "TwoKeys: AI Agents Can Act, but Should They Decide?", "summary": "An engineer built TwoKeys, a system that adds an approval layer between AI agents and consequential actions, ensuring that agents can propose but not execute until all responsible people approve the exact same version. The system binds approvals to immutable action hashes, evidence, and policy versions, preventing the dangerous case where two people approve different realities. The project was created for the All Things Agentic Hackathon and uses Gemini to compose role-specific approval surfaces while deterministic code handles authorization.", "body_md": "*I created this article for the purpose of entering the All Things Agentic Hackathon.*\n\nAn AI agent already has the API key.\n\nIt can read the campaign, change its status, and call Google Ads. Technically, it has everything it needs to activate a €30,000 launch campaign.\n\nBut did the company actually agree to do that?\n\nThat is the gap I built **TwoKeys** to explore.\n\nTechnical capability is not sufficient organizational consent.\n\nTwoKeys lets agents keep their tools and autonomy, but places a decision boundary in front of consequential actions. The agent may propose an action. It may explain the evidence. It may even prepare the exact API request. But the action does not run until every responsible person approves the same final version.\n\nThe short version is:\n\n```\nAgents act. People decide.\n```\n\nI deliberately built one concrete story instead of a generic “AI governance platform.”\n\nA Revenue Agent proposes a 14-day, €30,000 EU campaign in a Google Ads test account.\n\nThe business data, company, and people in the demo are synthetic. The local demo uses a simulated campaign. The production adapter is limited to a Google Ads test account, which has no billing and serves no ads.\n\nThat distinction matters. This is a proof of an authorization mechanism, not proof that an AI agent spent €30,000 or ran a live advertising campaign.\n\nAdding two approval buttons is easy.\n\nThe dangerous case is more subtle: two people can approve two different realities.\n\nImagine this sequence:\n\n```\nAna approves:  €30,000 campaign\nMarco changes: €30,000 campaign + safety condition\n```\n\nIf Ana’s approval silently carries over, the system can claim that both people agreed even though Ana never saw the final plan.\n\nTwoKeys treats every material change as a new immutable version. Each approval is bound to:\n\n```\naction hash\nevidence hash\npolicy version\naction version\nexpiry\n```\n\nThe central invariant is intentionally boring:\n\n```\nNo executor call unless every required role approved\nthe same action, evidence, policy, and current version.\n```\n\nWhen Marco adds the condition, the action hash changes. Ana’s approval becomes stale automatically. This is not a warning in the interface; it is a state transition enforced by deterministic code.\n\nThat was the core product insight for me: **approval is only meaningful when it is bound to what was actually approved.**\n\nThe proposing agent does not select its reviewers.\n\nTwoKeys resolves the required roles from the material properties of the action using deterministic policy. A small routine action may need no human approval. A campaign above the configured threshold resolves to Finance and CEO.\n\nThis matters because an agent should not be able to route around an inconvenient decision-maker by changing a prompt or tool argument.\n\nThe agent integration stays small:\n\n``` php\npropose_action(action, evidence) -> authorized | pending\nawait_decision(decision_id)      -> permission | denial | pending\n```\n\nThe repository exposes that seam over HTTP, MCP, and a Google ADK adapter. An existing agent can keep its current tools and call TwoKeys only when it is ready to commit the organization to an action.\n\nAna and Marco should not receive two generic dashboards.\n\nThey are answering different questions:\n\nBoth views still resolve from one server-owned fact model. The campaign, budget, evidence, policy, and action fingerprint are identical. Only the order and explanation change.\n\nGemini composes these role-specific surfaces by selecting and ordering components from an allowlisted A2UI catalog. It cannot invent HTML, JavaScript, calculations, or executable actions. The trusted renderer owns every material value.\n\nThe boundary is simple:\n\n```\nGemini explains and arranges.\nDeterministic code authorizes and executes.\n```\n\nThis separation also applies to memory. If Marco explicitly asks to see the smallest reversible pilot first on future launch decisions, TwoKeys can remember that preference for Marco’s later view. It does not change Ana’s interface, shared evidence, or company policy.\n\nPersonalization improves how a decision is presented. It must never quietly change what the organization is authorizing.\n\nAfter both people approve the final version, TwoKeys creates a short-lived, revocable, single-use execution permit. Internally, the project calls it an `ActionLease`\n\n.\n\nThe executor then:\n\nConsuming the permit before the external call is important. Provider calls can fail ambiguously: the client may time out after the provider accepted the request. A blind retry could execute the action twice.\n\nTwoKeys does not make that retry. Once the permit is consumed, recovery requires reading the external state and reconciling what happened.\n\nThe result is a small but useful guarantee:\n\n``` php\none approved version -> one execution attempt\n```\n\nThe system uses models where interpretation is useful, not where determinism is required.\n\nGemini can:\n\nGemini cannot:\n\nI also added a separate Gemma evaluation lane for detecting prompt-injection and personal-data signals in frozen evidence fixtures. Its output is schema-validated and measured, but the authority kernel never reads its verdict.\n\nThat prevents a common failure in agent projects: adding a “safety model” whose unverified opinion silently becomes the security boundary.\n\nTwoKeys has several logical responsibilities, but it does not need a fleet of microservices.\n\nThe intended Google Cloud shape is:\n\nThe same application serves the decision UI, approval API, authority kernel, agent seam, and executor. Firestore transactions serialize competing approval and lease operations.\n\nThe local application, deterministic kernel, adapters, and automated checks are implemented. A live Cloud Run deployment and retained proof of the real Google Ads test-account mutation are still pending, so I do not present them as completed results.\n\nThe visible button is the least interesting part. The real control is making sure an old “yes” cannot survive a new plan.\n\nA blank canvas gives the model too much control. An allowlisted catalog with server-owned data references still allows useful adaptation without letting facts drift.\n\nRemembering how Marco prefers to inspect a decision is helpful. Letting that preference modify shared facts or approval rules would undermine the entire system.\n\nA timeout does not grant permission to try again. Consume first, call once, read back, and reconcile.\n\nTwoKeys does not claim to secure every enterprise agent. It demonstrates one consequential action, one material change, one stale approval, one permission, and one blocked replay.\n\nThat is enough to make the boundary visible.\n\nTwoKeys currently requires Node.js 24.\n\n```\ngit clone https://github.com/migarci2/TwoKeys.git\ncd TwoKeys/web\nnpm ci\nLOCAL_DEMO_AUTH=true npm run dev\n```\n\nThen open:\n\n```\nhttp://localhost:3000/demo\n```\n\nDevelopment uses a deterministic surface fallback when no Gemini API key is configured, so the approval flow can be tested without provider credentials.\n\nRun the automated checks with:\n\n```\nnpm test\nnpm run lint\nnpm run build\n```\n\nThe next milestone is not more surface area. It is stronger evidence for the slice that already exists:\n\nOnly after that would I widen the action vocabulary or add more executors.\n\nAgents are becoming capable enough to commit companies to real actions. The answer cannot be to stop them before every harmless task, and it cannot be to treat one API key as permanent consent for everything they may invent later.\n\nThe organization needs a final key for the decisions that bind it.\n\nAgents act. People decide.", "url": "https://wpnews.pro/news/twokeys-ai-agents-can-act-but-should-they-decide", "canonical_source": "https://dev.to/xdarksyderx/twokeys-ai-agents-can-act-but-should-they-decide-a4i", "published_at": "2026-08-31 22:41:52+00:00", "updated_at": "2026-08-31 23:23:11.967696+00:00", "lang": "en", "topics": ["ai-agents", "ai-safety", "ai-policy", "ai-tools", "developer-tools"], "entities": ["TwoKeys", "Google Ads", "Gemini", "All Things Agentic Hackathon"], "alternates": {"html": "https://wpnews.pro/news/twokeys-ai-agents-can-act-but-should-they-decide", "markdown": "https://wpnews.pro/news/twokeys-ai-agents-can-act-but-should-they-decide.md", "text": "https://wpnews.pro/news/twokeys-ai-agents-can-act-but-should-they-decide.txt", "jsonld": "https://wpnews.pro/news/twokeys-ai-agents-can-act-but-should-they-decide.jsonld"}}