diff --git a/.github/workflows/authority-ops.yml b/.github/workflows/authority-ops.yml new file mode 100644 index 0000000..857b48d --- /dev/null +++ b/.github/workflows/authority-ops.yml @@ -0,0 +1,30 @@ +name: Authority Ops + +on: + push: + branches: [main, hackathon-authority-ops] + paths: + - 'hackathon/authority-ops/**' + - '.github/workflows/authority-ops.yml' + pull_request: + paths: + - 'hackathon/authority-ops/**' + - '.github/workflows/authority-ops.yml' + +jobs: + test: + runs-on: ubuntu-latest + defaults: + run: + working-directory: hackathon/authority-ops + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: hackathon/authority-ops/package.json + - run: npm install --no-audit --no-fund + - run: npm run build + - run: npm test + - run: npm run demo diff --git a/hackathon/authority-ops/ABOUT-PROJECT.md b/hackathon/authority-ops/ABOUT-PROJECT.md new file mode 100644 index 0000000..afbb46d --- /dev/null +++ b/hackathon/authority-ops/ABOUT-PROJECT.md @@ -0,0 +1,62 @@ +# Authority Ops — About the Project + +## What is Authority Ops? + +Authority Ops is a professional AI agent for invoice processing and simulated payments. It is designed around a simple operational rule: an agent may propose an action, but the agent does not get to decide its own authority. + +The agent processes invoices, proposes payment actions, and requests execution through an explicit authority layer. The authority contract defines what the agent may do, when human approval is required, and what must be blocked. + +## Who is it for? + +Authority Ops is designed for finance and operations teams that want to automate repetitive invoice work without giving an AI agent unrestricted permission to move money or operate business systems. + +The demonstrated workflow uses three tiers: + +- **$0–$500:** autonomous approval and simulated execution +- **$501–$1,000:** human approval required +- **Above $1,000:** denied before payment execution + +## Why does it matter? + +Traditional automation often focuses on whether a tool can perform an action. Agentic systems add a different problem: the model can reason about actions and choose tools dynamically. + +Authority Ops makes the missing boundary explicit: + +> **Proposal ≠ Authorization ≠ Execution ≠ Outcome** + +The result is an agent workflow where permission is evaluated independently from model reasoning, execution occurs only after authorization, and the system records evidence of what was requested and what happened. + +## How it works + +1. A Strands agent reads and reasons about an invoice. +2. The application derives transaction-critical fields from trusted invoice state. +3. The proposed payment action is evaluated by an Agent Authority integration using Passport, Contract, Gate, and Authority Score concepts. +4. The gate returns **ALLOW**, **ASK**, or **DENY**. +5. Only an authorized action may reach the simulated payment executor. +6. The workflow records an evidence chain covering request, evidence, proposal, authorization, approval, execution attempt, destination, and outcome. + +The implementation also includes regression tests for wrong-agent identity, authority-limit violations, invoice-context substitution, destination substitution, and action-digest integrity. + +## What was built for the hackathon + +Authority Ops is a new application built for the Agents for Humans Hackathon. It uses the existing open-source Agent Authority project as an authority-layer integration. The new application work includes the invoice workflow, Strands agent/tool integration, scenario data, browser UI, evidence-flow implementation, and red-team hardening. + +The payment flow is simulated. This is a hackathon reference application, not a production payment system or a hardened financial execution environment. + +## Built with + +- **Strands Agents TypeScript SDK** — agent orchestration, tool use, and model-driven workflow +- **Amazon Bedrock** — model provider for the live agent path +- **TypeScript / Node.js 22+** — application runtime +- **Agent Authority** — identity, delegated contracts, policy gates, authority scoring, and signed/evidence-oriented authorization concepts +- **Zod** — TypeScript schema validation +- **HTML / CSS / JavaScript** — browser presentation interface +- **GitHub Actions** — automated build and deterministic demo/test workflow + +## Track + +**Professional Agents** — an agent intended to make repetitive, judgment-heavy professional work faster while keeping consequential actions inside explicit authority boundaries. + +## License + +MIT diff --git a/hackathon/authority-ops/AWS-BUILDER-BLOG-DRAFT.md b/hackathon/authority-ops/AWS-BUILDER-BLOG-DRAFT.md new file mode 100644 index 0000000..0faeeee --- /dev/null +++ b/hackathon/authority-ops/AWS-BUILDER-BLOG-DRAFT.md @@ -0,0 +1,70 @@ +# Agents for Humans: Building an Authority-Aware Professional Agent with Strands Agents + +## Working thesis + +AI agents are good at proposing actions. Professional software needs a separate answer to a harder question: **is this exact action authorized?** + +For the Agents for Humans Hackathon, Authority Ops explores that boundary in a concrete accounts-payable workflow using Strands Agents for the agent layer and an explicit delegated-authority layer for transaction control. + +## The use case + +Imagine a finance team that wants an AI agent to process routine vendor invoices. + +The agent should be able to read an invoice, identify the vendor and amount, and request a payment. But the organization does not want the model to decide its own authority. + +Our demo contract therefore defines three tiers: + +- $500 or less: autonomous execution +- $501–$1,000: human review +- above $1,000: deny + +This turns an abstract safety principle into a visible business rule. + +## Why Strands Agents + +The application is implemented in TypeScript using the Strands Agents SDK. Strands provides the agent loop, model interaction, and custom tool mechanism; the authority layer remains an explicit application boundary around the business action. + +The important architectural choice is that the payment capability is not trusted merely because the model selected a tool. The application constructs the payment request from trusted invoice state and then asks the authority gate to evaluate that exact request. + +## The security lesson + +During development we deliberately tested an obvious failure mode: could an untrusted caller change the invoice amount and turn an $8,400 invoice into a $400 authorized payment? + +The first implementation exposed this weakness because the tool accepted an agent-supplied transaction amount. + +We hardened the flow so the payment request is derived from trusted application state using the invoice ID supplied to the tool. The same principle applies to vendor, currency, invoice ID, and destination. + +We also tested destination substitution: an authorized payment must not become authorized merely because its destination changes. + +The result is a stronger boundary: + +**model proposal → trusted application state → authorization decision → execution** + +## Evidence, not just logs + +The demo records a causal evidence chain: + +**REQUEST → EVIDENCE → PROPOSAL → AUTHORIZATION → APPROVAL → EXECUTION ATTEMPT → DESTINATION → OUTCOME** + +This distinction matters because a model response saying "pay the invoice" is not proof that an authorized payment happened. + +## What the demo shows + +1. A $480 invoice is allowed and submitted to the simulated payment system. +2. An $800 invoice enters the human-approval path and is submitted after approval. +3. An $8,400 invoice is denied before payment execution. +4. Red-team tests verify identity binding, amount binding, destination binding, and digest integrity. + +## What comes next + +The hackathon application is deliberately small. A production system would need durable state, stronger issuer trust, real human-approval infrastructure, hardened runtime isolation, real financial controls, and independent security review. + +The architectural idea is the part worth carrying forward: **an agent should not be its own authority boundary.** + +## Suggested closing + +Strands makes it easy to build an agent that can reason and act. Authority Ops asks what should happen one layer later: + +> When the agent proposes an action, what proves it was allowed to perform that exact action? + +That is the boundary we built and tested for this hackathon. diff --git a/hackathon/authority-ops/HACKATHON-SUBMISSION.md b/hackathon/authority-ops/HACKATHON-SUBMISSION.md new file mode 100644 index 0000000..cf83910 --- /dev/null +++ b/hackathon/authority-ops/HACKATHON-SUBMISSION.md @@ -0,0 +1,209 @@ +# Authority Ops — Submission Package + +## One-line pitch + +**Give a professional AI agent a permission slip—and make every attempted action prove what it was allowed to do.** + +## Project title + +Authority Ops + +## Track + +Professional Agents + +## Submission description + +### The problem + +AI agents are increasingly capable of taking actions inside business workflows, but capability is not the same thing as authorization. In accounts payable, an agent may correctly identify an invoice and propose a payment while still having no authority to move that amount, use that destination, or act without human review. + +Traditional demos often blur together what the model proposed, what the system permitted, what the tool executed, and what actually happened. That makes an agent difficult to trust. + +### Who it is for + +Authority Ops is designed for finance, operations, and IT teams that want AI agents to handle repetitive invoice workflows without giving those agents unrestricted transaction power. + +### What it does + +Authority Ops uses a Strands Agents TypeScript agent to process invoices and request simulated payments. Before execution, an explicit authority layer evaluates the action against the agent's identity and delegated contract. + +The demo contract creates three visible outcomes: + +- **$500 or less → ALLOW** — execute automatically. +- **$501–$1,000 → ASK** — require human approval. +- **Above $1,000 → DENY** — do not execute. + +The payment adapter also binds transaction-critical fields—invoice ID, amount, vendor, currency, and destination—to trusted application state instead of accepting those fields from the model as authority-bearing input. + +### Why it matters + +The design makes a simple but important distinction explicit: + +> **Proposal is not authorization. Authorization is not execution. Execution is not outcome.** + +The resulting evidence chain records the causal path: + +**REQUEST → EVIDENCE → PROPOSAL → AUTHORIZATION → APPROVAL → EXECUTION ATTEMPT → DESTINATION → OUTCOME** + +That pattern can be applied to other professional agents that need bounded, reviewable action authority. + +### What makes it different + +Authority Ops does not replace the agent framework. Strands handles the agent loop, reasoning, and tool use. The authority layer sits at the action boundary and evaluates whether a proposed operation is permitted before the business tool runs. + +The demo therefore focuses on the control point that is easy to miss in an agent architecture: the transition from **"the agent wants to do this"** to **"the system permits this exact action."** + +### AWS / Strands implementation + +The application is implemented in TypeScript with `@strands-agents/sdk` and includes a live Amazon Bedrock path using `BedrockModel`. A deterministic local path is also provided so judges can reproduce the core behavior without cloud credentials. + +The project can be built and tested from `hackathon/authority-ops` with Node.js 22+. + +### Demonstrated scenarios + +| Invoice | Amount | Gate | Outcome | +| --- | ---: | --- | --- | +| INV-1041 | $480 | ALLOW | Simulated payment submitted | +| INV-1042 | $800 | ASK | Approval recorded, then simulated payment submitted | +| INV-1043 | $8,400 | DENY | Payment execution blocked | + +### Security posture + +This is a hackathon reference application. Payments are simulated; this is not a production payment system or hardened hostile-code sandbox. The red-team hardening is intentionally included to demonstrate that critical transaction fields must come from trusted business state rather than model-generated values. + +## Demo video outline + +Target runtime: **4:30–4:50**. Keep the final upload below the 5-minute maximum. + +### 0:00–0:20 — Hook + +Show INV-1043 at $8,400. + +Say: + +> "I gave this AI agent authority to approve payments up to $1,000. Now watch what happens when it encounters an $8,400 invoice." + +### 0:20–0:50 — Problem + +Show the architecture and the three authority tiers. + +Say: + +> "An agent can know what should happen and still not be authorized to do it. We separate proposal, authorization, execution, and outcome so the boundary is enforceable and observable." + +### 0:50–1:20 — Architecture + +Highlight: + +**Strands Agent → Passport / Contract / Gate → business tool → Ledger** + +Explain that the payment tool is downstream of the authority decision. + +### 1:20–2:15 — ALLOW case + +Run INV-1041 ($480). + +Show: + +**ALLOW → simulated execution → receipt** + +Point out that the amount and destination are bound by trusted application state. + +### 2:15–3:10 — ASK case + +Run INV-1042 ($800). + +Show: + +**ASK → human approval → simulated execution → receipt** + +Pause briefly on the approval record. + +### 3:10–3:45 — DENY case + +Run INV-1043 ($8,400). + +Show: + +**DENY → payment tool not invoked** + +Say: + +> "The agent can propose the payment. It cannot grant itself the authority to execute it." + +### 3:45–4:20 — Red-team proof + +Show the test output for: + +- wrong agent identity +- trusted invoice amount binding +- destination substitution +- action digest integrity + +Say: + +> "We deliberately attacked the boundary during development. A caller cannot turn an $8,400 invoice into a $400 authorized transaction, and an authorized amount cannot be redirected to an unapproved destination." + +### 4:20–4:45 — Close + +Show the evidence chain. + +Say: + +> "Authority Ops demonstrates a reusable pattern for professional agents: give agents useful power, constrain that power explicitly, and leave evidence of what was requested, authorized, executed, and produced." + +## Judge-facing proof points + +### Technical Implementation + +- Real Strands Agents TypeScript SDK integration. +- Amazon Bedrock live path plus deterministic local test path. +- Custom tools and an explicit action-authorization boundary. +- Regression tests for identity, authority limits, trusted invoice binding, destination substitution, and action digest integrity. + +### Design + +- One focused professional workflow. +- Three easy-to-understand authority outcomes. +- Browser presentation UI plus CLI demo. +- Evidence chain makes authorization state visible instead of hiding it in logs. + +### Potential Impact + +- Targets repetitive accounts-payable work. +- Demonstrates a concrete pattern for safe delegation instead of a generic chatbot. +- The authority model is intentionally independent of one business workflow. + +### Creativity & Originality + +- Treats the authorization boundary itself as a product surface. +- Makes the distinction between model proposal and executable authority visible. +- Adds a security-focused red-team story to the demo rather than only showing the happy path. + +### Presentation + +- Start with the $8,400 denial, not the architecture diagram. +- Keep the authority policy visible throughout the demo. +- Show all three outcomes end-to-end. +- End with evidence, not another model response. + +## Final pre-submission checklist + +- [ ] Devpost title: Authority Ops +- [ ] Track: Professional Agents +- [ ] Public repository URL added +- [ ] Repository shows an MIT license +- [ ] README present and install instructions tested +- [ ] Architecture diagram linked and visible +- [ ] Demo video is public and under 5 minutes +- [ ] Video demonstrates the working project and covers problem, audience, and importance +- [ ] AWS Builder ID entered +- [ ] Live demo link added, if stable +- [ ] Any pre-existing non-standard code/work disclosed +- [ ] No secrets, credentials, or private data in the repository +- [ ] Final submission submitted before September 14, 2026 at 5:00 PM PDT + +## Optional bonus + +The official rules allow up to **0.6 bonus points** for public AWS Builder blog posts describing the journey building and implementing AWS for the hackathon. Up to three pieces can contribute 0.2 each; the title must use **Agents for Humans**. diff --git a/hackathon/authority-ops/HACKATHON.md b/hackathon/authority-ops/HACKATHON.md new file mode 100644 index 0000000..572bdb8 --- /dev/null +++ b/hackathon/authority-ops/HACKATHON.md @@ -0,0 +1,7 @@ +# Hackathon project disclosure + +Authority Ops is the new application submitted for the Agents for Humans Hackathon. + +The application uses the Agent Authority protocol as an authority-layer integration. Agent Authority remains a separate open-source project. + +The new work in this application is the professional invoice workflow, Strands agent/tool integration, scenario data, UI, and evidence-flow implementation. diff --git a/hackathon/authority-ops/README.md b/hackathon/authority-ops/README.md new file mode 100644 index 0000000..61e4911 --- /dev/null +++ b/hackathon/authority-ops/README.md @@ -0,0 +1,107 @@ +# Authority Ops + +**A professional AI agent that can act—but only inside explicit delegated authority.** + +Authority Ops is a hackathon application for the **Agents for Humans Hackathon**. It uses the Strands Agents TypeScript SDK for the agent layer and an Agent Authority integration for identity, contracts, policy gates, and authority evidence. + +## The demo + +The agent processes vendor invoices and can request a simulated payment. + +The key boundary is: + +> **Proposal is not authorization. Authorization is not execution. Execution is not outcome.** + +The contract gives the agent a **$1,000 delegated payment limit**: + +- **$500 or less → ALLOW** and execute automatically +- **$501–$1,000 → ASK** and require human approval +- **Above $1,000 → DENY** and do not invoke the payment tool + +| Invoice | Amount | Authority result | What happens | +| --- | ---: | --- | --- | +| INV-1041 | $480 | ALLOW | Simulated payment is submitted | +| INV-1042 | $800 | ASK → APPROVE | Human approval is recorded, then payment is submitted | +| INV-1043 | $8,400 | DENY | No payment execution is permitted | + +The intentionally repetitive workflow makes the authority boundary visible instead of burying it in agent output. + +## Architecture + +```text + ┌──────────────────┐ + │ Strands Agent │ + │ reason + tools │ + └────────┬─────────┘ + │ proposal + ▼ + ┌──────────────────────────┐ + │ Agent Authority │ + │ Passport • Contract │ + │ Gate • Authority Score │ + └────────────┬─────────────┘ + │ + ┌────────────┼────────────┐ + ▼ ▼ ▼ + ALLOW ASK DENY + │ │ │ + ▼ ▼ ▼ + execute approval blocked + │ │ + │ ▼ + │ approve + │ │ + └──────┬─────┘ + ▼ + execute + │ + ▼ + Ledger receipt + +Ledger chain: +REQUEST → EVIDENCE → PROPOSAL → AUTHORIZATION → +APPROVAL → EXECUTION ATTEMPT → DESTINATION → OUTCOME +``` + +## Run the deterministic demo + +The deterministic demo requires only Node.js and TypeScript: + +```bash +npm install +npm run demo +npm test +``` + +This path is designed to be repeatable for reviewers and does not require cloud credentials. + +## Browser demo + +Open `web/index.html` in a browser to show the three authority outcomes as a presentation UI. It is intentionally static so a reviewer can inspect and run it without a backend. + +## Run the live Strands agent + +The live path uses the Strands Agents TypeScript SDK and Amazon Bedrock. Current Strands TypeScript guidance requires Node.js 22+ and supports `Agent`, custom tools, and `BedrockModel`. Configure AWS credentials and model access before running the live path. + +```bash +npm install +npm run build +export AWS_REGION=us-east-1 +npm run live -- "Process invoice INV-1043. Read the invoice and request payment only if authorized." +``` + +The payment tool is deliberately authority-aware: the agent cannot bypass the gate by changing its own narrative. + +## Why this matters + +Professional agents are moving from producing text to operating systems, workflows, and business tools. The useful question is no longer only **"Can the model do this?"** It is also **"Was the agent authorized to do this, under which contract, and what evidence proves what happened?"** + +Authority Ops demonstrates one concrete pattern for answering those questions without replacing the underlying agent framework. + +## Security note + +This is a hackathon reference application, not a production payment system or a hardened sandbox. The payment system is simulated. Agent Authority's security limitations and threat model remain applicable. + +## License + +MIT diff --git a/hackathon/authority-ops/assets/architecture.svg b/hackathon/authority-ops/assets/architecture.svg new file mode 100644 index 0000000..67b9d50 --- /dev/null +++ b/hackathon/authority-ops/assets/architecture.svg @@ -0,0 +1,29 @@ + + + Authority Ops + Strands agent → delegated authority → controlled execution → evidence + + Strands Agent + reason • propose • use tools + + Agent Authority + Passport + Contract + Gate • Score • Evidence + + + + + + + ALLOW + ASK + DENY + Ledger Receipt + request → outcome + + + + + Proposal ≠ Authorization ≠ Execution ≠ Outcome + diff --git a/hackathon/authority-ops/docs/ARCHITECTURE.md b/hackathon/authority-ops/docs/ARCHITECTURE.md new file mode 100644 index 0000000..0fefa47 --- /dev/null +++ b/hackathon/authority-ops/docs/ARCHITECTURE.md @@ -0,0 +1,41 @@ +# Authority Ops Architecture + +## Components + +### Strands Agent + +The agent provides reasoning and tool selection. Its tools are ordinary application capabilities; the payment tool is not itself the security boundary. + +### Agent Authority integration + +The application creates an agent Passport and a time-bounded Contract. The Contract grants constrained `payments:create` capabilities for three outcomes: autonomous allow, human review, and deny above the delegated limit. Every payment request is converted into an action request and evaluated before execution. + +### Ledger + +The receipt intentionally distinguishes: + +1. request +2. evidence +3. model proposal +4. authorization +5. human approval +6. execution attempt +7. destination +8. outcome + +This prevents the common collapse of “the model proposed it” and “the system executed it” into a single event. + +## Demonstrated threats + +- wrong agent identity +- over-limit payment request +- missing capability +- approval boundary +- auditability of blocked actions + +## Non-goals + +- real payment movement +- generalized ERP integration +- kernel-level sandboxing +- production financial controls diff --git a/hackathon/authority-ops/package.json b/hackathon/authority-ops/package.json new file mode 100644 index 0000000..8c12370 --- /dev/null +++ b/hackathon/authority-ops/package.json @@ -0,0 +1,23 @@ +{ + "name": "authority-ops", + "version": "0.1.0", + "private": false, + "type": "module", + "description": "A professional invoice agent demonstrating explicit delegated authority with Strands Agents and Agent Authority.", + "license": "MIT", + "engines": { "node": ">=22" }, + "scripts": { + "build": "tsc -p tsconfig.json", + "demo": "tsc -p tsconfig.demo.json && node dist-demo/demo.js", + "test": "tsc -p tsconfig.demo.json && node dist-demo/test.js", + "live": "tsc -p tsconfig.json && node dist/live-agent.js" + }, + "dependencies": { + "@strands-agents/sdk": "^1.16.0", + "zod": "^4.1.5" + }, + "devDependencies": { + "@types/node": "^24.3.0", + "typescript": "^5.9.2" + } +} diff --git a/hackathon/authority-ops/scenarios/invoices.json b/hackathon/authority-ops/scenarios/invoices.json new file mode 100644 index 0000000..cc333e7 --- /dev/null +++ b/hackathon/authority-ops/scenarios/invoices.json @@ -0,0 +1,29 @@ +[ + { + "invoiceId": "INV-1041", + "vendorId": "V-021", + "vendorName": "Northwind Industrial", + "amount": 480, + "currency": "USD", + "description": "Maintenance supplies", + "source": "ap:inbox/INV-1041.pdf" + }, + { + "invoiceId": "INV-1042", + "vendorId": "V-021", + "vendorName": "Northwind Industrial", + "amount": 800, + "currency": "USD", + "description": "Quarterly service renewal", + "source": "ap:inbox/INV-1042.pdf" + }, + { + "invoiceId": "INV-1043", + "vendorId": "V-021", + "vendorName": "Northwind Industrial", + "amount": 8400, + "currency": "USD", + "description": "Emergency equipment replacement", + "source": "ap:inbox/INV-1043.pdf" + } +] diff --git a/hackathon/authority-ops/src/authority.ts b/hackathon/authority-ops/src/authority.ts new file mode 100644 index 0000000..5149c27 --- /dev/null +++ b/hackathon/authority-ops/src/authority.ts @@ -0,0 +1,111 @@ +import { createHash, randomUUID } from 'node:crypto'; +import type { ActionRequest, AgentContract, AgentPassport, Capability, GateResult } from './domain.js'; + +function digest(value: unknown): string { + const canonicalize = (v: unknown): string => { + if (v === null || typeof v !== 'object') return JSON.stringify(v); + if (Array.isArray(v)) return `[${v.map(canonicalize).join(',')}]`; + const record = v as Record; + return `{${Object.keys(record).sort().map(k => `${JSON.stringify(k)}:${canonicalize(record[k])}`).join(',')}}`; + }; + return createHash('sha256').update(canonicalize(value), 'utf8').digest('hex'); +} + +function active(start: string, end: string, now: Date): boolean { + const a = new Date(start).getTime(); + const b = new Date(end).getTime(); + return Number.isFinite(a) && Number.isFinite(b) && now.getTime() >= a && now.getTime() <= b; +} + +function matches(constraints: Record | undefined, input: Record | undefined): boolean { + if (!constraints) return true; + if (!input) return false; + return Object.entries(constraints).every(([key, expected]) => { + const actual = key === 'amount_lte' ? input.amount : input[key]; + if (key === 'amount_lte' && typeof expected === 'number' && typeof actual === 'number') return actual <= expected; + if (Array.isArray(expected)) return expected.includes(actual); + if (expected && typeof expected === 'object' && actual && typeof actual === 'object') { + return Object.entries(expected as Record).every(([k, v]) => (actual as Record)[k] === v); + } + return actual === expected; + }); +} + +export interface AuthorityEvent { + eventId: string; + timestamp: string; + agentId: string; + contractId: string; + actionDigest: string; + request: ActionRequest; + decision: GateResult['decision']; + reasons: string[]; +} + +export class AgentAuthorityAdapter { + constructor(private readonly passport: AgentPassport, private readonly contract: AgentContract) {} + + evaluate(request: ActionRequest, now = new Date()): { result: GateResult; event: AuthorityEvent } { + const result = this.check(request, now); + const event: AuthorityEvent = { + eventId: `event_${randomUUID()}`, + timestamp: now.toISOString(), + agentId: request.agentId, + contractId: this.contract.contractId, + actionDigest: digest(request), + request, + decision: result.decision, + reasons: result.reasons, + }; + return { result, event }; + } + + private check(request: ActionRequest, now: Date): GateResult { + if (request.agentId !== this.passport.agentId) return { decision: 'deny', reasons: ['Agent identity does not match Passport.'] }; + if (new Date(this.passport.expiresAt).getTime() < now.getTime()) return { decision: 'deny', reasons: ['Passport is expired.'] }; + if (request.agentId !== this.contract.subjectAgentId) return { decision: 'deny', reasons: ['Agent identity does not match Contract subject.'] }; + if (!active(this.contract.createdAt, this.contract.expiresAt, now)) return { decision: 'deny', reasons: ['Contract is not active.'] }; + + const capability = this.contract.capabilities.find((c) => c.resource === request.resource && c.actions.includes(request.action) && matches(c.constraints, request.input)); + if (!capability) return { decision: 'deny', reasons: ['No matching capability or constraints were not satisfied.'] }; + const decision = capability.decision ?? 'allow'; + const reason = decision === 'ask' ? 'Matched capability is human-approval gated.' : `Matched capability ${request.resource}:${request.action}.`; + return { decision, matchedCapability: capability, reasons: [reason] }; + } +} + +export function authorityScore(contract: AgentContract): number { + let score = 100; + if (contract.capabilities.some((c) => ['process', 'shell', 'os'].includes(c.resource))) score -= 30; + if (contract.capabilities.some((c) => c.resource === 'payments' && !c.constraints)) score -= 25; + if (contract.capabilities.every((c) => c.constraints && Object.keys(c.constraints).length > 0)) score += 8; + if (contract.capabilities.some((c) => c.decision === 'ask')) score += 5; + return Math.max(0, Math.min(100, score)); +} + +export function buildContract(agentId: string, maxPayment: number, paymentDestination = 'payments:demo-ledger'): AgentContract { + const now = new Date(); + const expiry = new Date(now.getTime() + 24 * 60 * 60 * 1000); + const autonomous: Capability = { + resource: 'payments', + actions: ['create'], + constraints: { currency: 'USD', amount_lte: Math.min(500, maxPayment), destination: paymentDestination }, + decision: 'allow', + }; + const review: Capability = { + resource: 'payments', + actions: ['create'], + constraints: { currency: 'USD', amount_lte: maxPayment, destination: paymentDestination }, + decision: 'ask', + }; + return { + version: '0.1', + contractId: `contract_${randomUUID()}`, + subjectAgentId: agentId, + issuer: 'authority-ops-demo', + purpose: 'Process vendor invoices within delegated spending authority.', + createdAt: now.toISOString(), + expiresAt: expiry.toISOString(), + capabilities: [autonomous, review], + }; +} diff --git a/hackathon/authority-ops/src/demo.ts b/hackathon/authority-ops/src/demo.ts new file mode 100644 index 0000000..149ee4e --- /dev/null +++ b/hackathon/authority-ops/src/demo.ts @@ -0,0 +1,110 @@ +import { createHash } from 'node:crypto'; +import { AgentAuthorityAdapter, authorityScore, buildContract } from './authority.js'; +import { makeReceipt, printReceipt, type LedgerEvent } from './ledger.js'; +import { checkInvoicePolicy } from './policy.js'; +import type { AgentPassport, Invoice, ToolInvocation } from './domain.js'; + +const agentId = 'agent_ap_ops'; +const passport: AgentPassport = { + version: '0.1', passportId: 'passport_demo_ap_ops', agentId, issuer: 'authority-ops-demo', + expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), +}; +const contract = buildContract(agentId, 1000); +const authority = new AgentAuthorityAdapter(passport, contract); + +const invoices: Invoice[] = [ + { invoiceId: 'INV-1041', vendorId: 'V-021', vendorName: 'Northwind Industrial', amount: 480, currency: 'USD', description: 'Maintenance supplies', source: 'ap:inbox/INV-1041.pdf' }, + { invoiceId: 'INV-1042', vendorId: 'V-021', vendorName: 'Northwind Industrial', amount: 800, currency: 'USD', description: 'Quarterly service renewal', source: 'ap:inbox/INV-1042.pdf' }, + { invoiceId: 'INV-1043', vendorId: 'V-021', vendorName: 'Northwind Industrial', amount: 8400, currency: 'USD', description: 'Emergency equipment replacement', source: 'ap:inbox/INV-1043.pdf' }, +]; + +function digest(value: unknown): string { + const canonicalize = (v: unknown): string => { + if (v === null || typeof v !== 'object') return JSON.stringify(v); + if (Array.isArray(v)) return `[${v.map(canonicalize).join(',')}]`; + const record = v as Record; + return `{${Object.keys(record).sort().map(k => `${JSON.stringify(k)}:${canonicalize(record[k])}`).join(',')}}`; + }; + return createHash('sha256').update(canonicalize(value), 'utf8').digest('hex'); +} + +function approvePayment(args: { + event: { decision: string; actionDigest: string }; + request: { agentId: string; resource: string; action: string; input?: Record }; + approver: string; +}): { approved: boolean; reason: string; approvalId: string } { + const amount = args.request.input?.amount; + const expectedDigest = digest(args.request); + if (args.event.decision !== 'ask') return { approved: false, reason: 'Approval is only valid for ASK decisions.', approvalId: 'none' }; + if (expectedDigest !== args.event.actionDigest) return { approved: false, reason: 'Approval does not match the exact authorized action.', approvalId: 'none' }; + if (args.request.agentId !== agentId || args.request.resource !== 'payments' || args.request.action !== 'create') { + return { approved: false, reason: 'Approval target does not match the payment action.', approvalId: 'none' }; + } + if (typeof amount !== 'number' || amount <= 500 || amount > 1000) { + return { approved: false, reason: 'Approval amount is outside the human-review tier.', approvalId: 'none' }; + } + return { approved: true, reason: `Approved by ${args.approver} for this exact action.`, approvalId: `approval_${args.event.actionDigest.slice(0, 12)}` }; +} + +console.log('AUTHORITY OPS'); +console.log('Professional invoice agent + delegated authority demo'); +console.log(`Authority Score: ${authorityScore(contract)}/100`); +console.log('Delegated payment limit: $1,000 USD'); +console.log('Autonomous: ≤ $500 | Human approval: $501–$1,000 | Deny: > $1,000'); + +for (const invoice of invoices) { + console.log('\n══════════════════════════════════════════'); + console.log(`INVOICE ${invoice.invoiceId} ${invoice.vendorName} $${invoice.amount.toLocaleString()}`); + console.log('──────────────────────────────────────────'); + console.log(`Policy: ${checkInvoicePolicy(invoice).reason}`); + + const proposal = { + action: 'payments:create', invoiceId: invoice.invoiceId, amount: invoice.amount, + currency: invoice.currency, vendorId: invoice.vendorId, destination: 'payments:demo-ledger', + }; + console.log(`Proposal: payments:create($${invoice.amount.toLocaleString()})`); + + const { result, event } = authority.evaluate({ + agentId, resource: 'payments', action: 'create', input: { currency: invoice.currency, amount: invoice.amount } + }); + console.log(`Gate: ${result.decision.toUpperCase()} — ${result.reasons.join(' ')}`); + + const tools: ToolInvocation[] = [ + { name: 'read_invoice', input: { invoiceId: invoice.invoiceId }, output: { extracted: true }, resultStatus: 'success' }, + { name: 'lookup_vendor', input: { vendorId: invoice.vendorId }, output: { active: true }, resultStatus: 'success' }, + ]; + const extras: LedgerEvent[] = [{ type: 'proposal', at: event.timestamp, payload: proposal }]; + + if (result.decision === 'allow') { + console.log('Execution: simulated payment submitted.'); + tools.push({ name: 'request_payment', input: proposal, output: { paymentId: `PAY-DEMO-${invoice.invoiceId.slice(-4)}`, status: 'submitted' }, resultStatus: 'success' }); + extras.push({ type: 'execution_attempt', at: new Date().toISOString(), payload: { tool: 'request_payment', status: 'submitted' } }); + extras.push({ type: 'destination', at: new Date().toISOString(), payload: { destination: proposal.destination } }); + extras.push({ type: 'outcome', at: new Date().toISOString(), payload: { status: 'completed', paymentId: `PAY-DEMO-${invoice.invoiceId.slice(-4)}` } }); + } else if (result.decision === 'ask') { + console.log('Approval: human review requested.'); + const approval = approvePayment({ + event, + request: { agentId, resource: 'payments', action: 'create', input: { currency: invoice.currency, amount: invoice.amount } }, + approver: 'finance.manager@example.com' + }); + console.log(`Approval: ${approval.approved ? 'APPROVED' : 'REJECTED'} — ${approval.reason}`); + extras.push({ type: 'approval', at: new Date().toISOString(), payload: { approvalId: approval.approvalId, status: approval.approved ? 'approved' : 'rejected', approver: 'finance.manager@example.com', actionDigest: event.actionDigest } }); + if (approval.approved) { + console.log('Execution: simulated payment submitted after approval.'); + tools.push({ name: 'request_payment', input: proposal, output: { paymentId: `PAY-DEMO-${invoice.invoiceId.slice(-4)}`, status: 'submitted' }, resultStatus: 'success' }); + extras.push({ type: 'execution_attempt', at: new Date().toISOString(), payload: { tool: 'request_payment', status: 'submitted', approvalId: approval.approvalId } }); + extras.push({ type: 'destination', at: new Date().toISOString(), payload: { destination: proposal.destination } }); + extras.push({ type: 'outcome', at: new Date().toISOString(), payload: { status: 'completed', paymentId: `PAY-DEMO-${invoice.invoiceId.slice(-4)}`, approvalId: approval.approvalId } }); + } else { + extras.push({ type: 'outcome', at: new Date().toISOString(), payload: { status: 'approval_rejected', reason: approval.reason } }); + } + } else { + console.log('Execution: BLOCKED — no payment tool invocation permitted.'); + extras.push({ type: 'execution_attempt', at: new Date().toISOString(), payload: { tool: 'request_payment', status: 'blocked' } }); + extras.push({ type: 'destination', at: new Date().toISOString(), payload: { destination: proposal.destination, reached: false } }); + extras.push({ type: 'outcome', at: new Date().toISOString(), payload: { status: 'blocked', reason: result.reasons[0] } }); + } + + printReceipt(makeReceipt(invoice, event, tools, extras)); +} diff --git a/hackathon/authority-ops/src/domain.ts b/hackathon/authority-ops/src/domain.ts new file mode 100644 index 0000000..2fc4445 --- /dev/null +++ b/hackathon/authority-ops/src/domain.ts @@ -0,0 +1,58 @@ +export type Decision = 'allow' | 'ask' | 'deny'; + +export interface Capability { + resource: string; + actions: string[]; + constraints?: Record; + decision?: Decision; +} + +export interface AgentPassport { + version: '0.1'; + passportId: string; + agentId: string; + issuer: string; + expiresAt: string; +} + +export interface AgentContract { + version: '0.1'; + contractId: string; + subjectAgentId: string; + issuer: string; + purpose: string; + createdAt: string; + expiresAt: string; + capabilities: Capability[]; + approvals?: { requiredFor: string[] }; +} + +export interface ActionRequest { + agentId: string; + resource: string; + action: string; + input?: Record; +} + +export interface GateResult { + decision: Decision; + reasons: string[]; + matchedCapability?: Capability; +} + +export interface Invoice { + invoiceId: string; + vendorId: string; + vendorName: string; + amount: number; + currency: 'USD'; + description: string; + source: string; +} + +export interface ToolInvocation { + name: string; + input: Record; + output?: Record; + resultStatus: 'success' | 'failure' | 'blocked' | 'unknown'; +} diff --git a/hackathon/authority-ops/src/ledger.ts b/hackathon/authority-ops/src/ledger.ts new file mode 100644 index 0000000..63288a7 --- /dev/null +++ b/hackathon/authority-ops/src/ledger.ts @@ -0,0 +1,43 @@ +import { createHash } from 'node:crypto'; +import type { Invoice, ToolInvocation } from './domain.js'; +import type { AuthorityEvent } from './authority.js'; + +export type LedgerEvent = + | { type: 'request'; at: string; payload: Record } + | { type: 'evidence'; at: string; payload: Record } + | { type: 'proposal'; at: string; payload: Record } + | { type: 'authorization'; at: string; payload: Record } + | { type: 'approval'; at: string; payload: Record } + | { type: 'execution_attempt'; at: string; payload: Record } + | { type: 'destination'; at: string; payload: Record } + | { type: 'outcome'; at: string; payload: Record }; + +export interface LedgerReceipt { + receiptId: string; + invoice: Invoice; + events: LedgerEvent[]; + tools: ToolInvocation[]; + receiptHash: string; +} + +export function makeReceipt(invoice: Invoice, authority: AuthorityEvent, tools: ToolInvocation[], extras: LedgerEvent[] = []): LedgerReceipt { + const proposal = extras.filter((e) => e.type === 'proposal'); + const postAuthorization = extras.filter((e) => e.type !== 'proposal'); + const events: LedgerEvent[] = [ + { type: 'request', at: authority.timestamp, payload: { invoiceId: invoice.invoiceId, source: invoice.source } }, + { type: 'evidence', at: authority.timestamp, payload: { vendorId: invoice.vendorId, vendorName: invoice.vendorName, amount: invoice.amount, description: invoice.description } }, + ...proposal, + { type: 'authorization', at: authority.timestamp, payload: { decision: authority.decision, reasons: authority.reasons, actionDigest: authority.actionDigest } }, + ...postAuthorization, + ]; + const base = { receiptId: `receipt_${authority.eventId}`, invoice, events, tools }; + const receiptHash = createHash('sha256').update(JSON.stringify(base)).digest('hex'); + return { ...base, receiptHash }; +} + +export function printReceipt(receipt: LedgerReceipt): void { + console.log('\nLEDGER RECEIPT'); + console.log('────────────────────────────────────────'); + for (const event of receipt.events) console.log(`${event.type.padEnd(20)} ${JSON.stringify(event.payload)}`); + console.log(`receipt_hash ${receipt.receiptHash}`); +} diff --git a/hackathon/authority-ops/src/live-agent.ts b/hackathon/authority-ops/src/live-agent.ts new file mode 100644 index 0000000..5b479a8 --- /dev/null +++ b/hackathon/authority-ops/src/live-agent.ts @@ -0,0 +1,94 @@ +import { Agent, BedrockModel, tool } from '@strands-agents/sdk'; +import { z } from 'zod'; +import { AgentAuthorityAdapter, authorityScore, buildContract } from './authority.js'; +import type { AgentPassport, Invoice } from './domain.js'; + +const agentId = 'agent_ap_ops'; +const paymentDestination = 'payments:demo-ledger'; +const passport: AgentPassport = { + version: '0.1', passportId: 'passport_live_ap_ops', agentId, issuer: 'authority-ops-demo', + expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), +}; +const contract = buildContract(agentId, 1000, paymentDestination); +const authority = new AgentAuthorityAdapter(passport, contract); + +const invoices: Invoice[] = [ + { invoiceId: 'INV-1041', vendorId: 'V-021', vendorName: 'Northwind Industrial', amount: 480, currency: 'USD', description: 'Maintenance supplies', source: 'ap:inbox/INV-1041.pdf' }, + { invoiceId: 'INV-1042', vendorId: 'V-021', vendorName: 'Northwind Industrial', amount: 800, currency: 'USD', description: 'Quarterly service renewal', source: 'ap:inbox/INV-1042.pdf' }, + { invoiceId: 'INV-1043', vendorId: 'V-021', vendorName: 'Northwind Industrial', amount: 8400, currency: 'USD', description: 'Emergency equipment replacement', source: 'ap:inbox/INV-1043.pdf' }, +]; + +const readInvoice = tool({ + name: 'read_invoice', + description: 'Read a vendor invoice from the demo inbox and return normalized fields.', + inputSchema: z.object({ invoiceId: z.string() }), + callback: ({ invoiceId }) => { + const invoice = invoices.find((x) => x.invoiceId === invoiceId); + if (!invoice) throw new Error(`Unknown invoice ${invoiceId}`); + return invoice; + }, +}); + +const requestPayment = tool({ + name: 'request_payment', + description: 'Request payment for a known invoice. Invoice amount, vendor, currency, and destination are derived from trusted application state and evaluated by the authority gate.', + inputSchema: z.object({ invoiceId: z.string() }), + callback: ({ invoiceId }) => { + const invoice = invoices.find((x) => x.invoiceId === invoiceId); + if (!invoice) throw new Error(`Unknown invoice ${invoiceId}`); + + const request = { + agentId, + resource: 'payments', + action: 'create', + input: { + invoiceId: invoice.invoiceId, + vendorId: invoice.vendorId, + currency: invoice.currency, + amount: invoice.amount, + destination: paymentDestination, + }, + }; + + const { result, event } = authority.evaluate(request); + return { + invoiceId, + vendorId: invoice.vendorId, + amount: invoice.amount, + currency: invoice.currency, + destination: paymentDestination, + decision: result.decision, + reasons: result.reasons, + authorityEventId: event.eventId, + authorityActionDigest: event.actionDigest, + authorityScore: authorityScore(contract), + executed: result.decision === 'allow', + }; + }, +}); + +const bedrockModel = new BedrockModel({ + modelId: process.env.STRANDS_MODEL_ID ?? 'global.anthropic.claude-sonnet-4-6', + region: process.env.AWS_REGION ?? 'us-east-1', + temperature: 0.2, +}); + +const agent = new Agent({ + model: bedrockModel, + tools: [readInvoice, requestPayment], + systemPrompt: [ + 'You are Authority Ops, a professional accounts-payable operations agent.', + 'Inspect invoices and request payment only through the request_payment tool.', + 'Never treat your own proposal as authorization.', + 'Never invent or override invoice amount, vendor, currency, invoice identity, or payment destination.', + 'The request_payment tool derives authoritative payment fields from the trusted invoice record and enforces the delegated authority contract.', + 'For the selected invoice, explain the proposal, authority decision, and outcome.', + ].join(' '), +}); + +const prompt = process.argv.slice(2).join(' ') || 'Process invoice INV-1043. Read the invoice and request payment only if authorized.'; +console.log(`Authority Ops live agent — authority score ${authorityScore(contract)}/100`); +console.log(`Task: ${prompt}`); +const result = await agent.invoke(prompt); +console.log('\nFINAL AGENT RESPONSE\n'); +console.log(result); diff --git a/hackathon/authority-ops/src/node-shims.d.ts b/hackathon/authority-ops/src/node-shims.d.ts new file mode 100644 index 0000000..0c16fce --- /dev/null +++ b/hackathon/authority-ops/src/node-shims.d.ts @@ -0,0 +1,4 @@ +declare module 'node:crypto' { + export function createHash(algorithm: string): { update(data: string, encoding?: string): { digest(encoding: string): string } }; + export const randomUUID: () => string; +} diff --git a/hackathon/authority-ops/src/payment.ts b/hackathon/authority-ops/src/payment.ts new file mode 100644 index 0000000..e117114 --- /dev/null +++ b/hackathon/authority-ops/src/payment.ts @@ -0,0 +1,16 @@ +import type { ActionRequest, Invoice } from './domain.js'; + +export function buildTrustedPaymentRequest(agentId: string, invoice: Invoice, destination: string): ActionRequest { + return { + agentId, + resource: 'payments', + action: 'create', + input: { + invoiceId: invoice.invoiceId, + vendorId: invoice.vendorId, + currency: invoice.currency, + amount: invoice.amount, + destination, + }, + }; +} diff --git a/hackathon/authority-ops/src/policy.ts b/hackathon/authority-ops/src/policy.ts new file mode 100644 index 0000000..905c05c --- /dev/null +++ b/hackathon/authority-ops/src/policy.ts @@ -0,0 +1,15 @@ +import type { Invoice } from './domain.js'; + +export interface PolicyCheck { + allowedVendor: boolean; + amountWithinReviewThreshold: boolean; + reason: string; +} + +export function checkInvoicePolicy(invoice: Invoice): PolicyCheck { + const allowedVendor = invoice.vendorId !== 'V-099'; + const amountWithinReviewThreshold = invoice.amount <= 1000; + if (!allowedVendor) return { allowedVendor, amountWithinReviewThreshold, reason: 'Vendor is blocked by policy.' }; + if (!amountWithinReviewThreshold) return { allowedVendor, amountWithinReviewThreshold, reason: 'Invoice exceeds the normal autonomous processing threshold.' }; + return { allowedVendor, amountWithinReviewThreshold, reason: 'Invoice is within normal processing policy.' }; +} diff --git a/hackathon/authority-ops/src/test.ts b/hackathon/authority-ops/src/test.ts new file mode 100644 index 0000000..e19d5d2 --- /dev/null +++ b/hackathon/authority-ops/src/test.ts @@ -0,0 +1,51 @@ +function assertEqual(actual: unknown, expected: unknown, message = 'assertion failed'): void { + if (actual !== expected) throw new Error(`${message}: expected ${String(expected)}, got ${String(actual)}`); +} + +import { AgentAuthorityAdapter, buildContract } from './authority.js'; +import { buildTrustedPaymentRequest } from './payment.js'; +import type { AgentPassport, Invoice } from './domain.js'; + +const agentId = 'agent_test'; +const paymentDestination = 'payments:demo-ledger'; +const passport: AgentPassport = { + version: '0.1', passportId: 'passport_test', agentId, issuer: 'test', + expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(), +}; +const authority = new AgentAuthorityAdapter(passport, buildContract(agentId, 1000, paymentDestination)); + +const lowInvoice: Invoice = { + invoiceId: 'INV-1041', vendorId: 'V-021', vendorName: 'Northwind Industrial', amount: 480, + currency: 'USD', description: 'Maintenance supplies', source: 'ap:inbox/INV-1041.pdf' +}; +const highInvoice: Invoice = { + invoiceId: 'INV-1043', vendorId: 'V-021', vendorName: 'Northwind Industrial', amount: 8400, + currency: 'USD', description: 'Emergency equipment replacement', source: 'ap:inbox/INV-1043.pdf' +}; + +const low = authority.evaluate(buildTrustedPaymentRequest(agentId, lowInvoice, paymentDestination)); +assertEqual(low.result.decision, 'allow', 'Autonomous tier should allow a trusted invoice at or below $500.'); +assertEqual(low.event.request.input?.amount, 480); +assertEqual(low.event.request.input?.invoiceId, 'INV-1041'); + +const review = authority.evaluate({ agentId, resource: 'payments', action: 'create', input: { currency: 'USD', amount: 800, destination: paymentDestination } }); +assertEqual(review.result.decision, 'ask', 'Review tier should require human approval.'); + +const wrongAgent = authority.evaluate({ agentId: 'other', resource: 'payments', action: 'create', input: { currency: 'USD', amount: 500, destination: paymentDestination } }); +assertEqual(wrongAgent.result.decision, 'deny', 'A different agent identity must be denied.'); + +const overLimit = authority.evaluate(buildTrustedPaymentRequest(agentId, highInvoice, paymentDestination)); +assertEqual(overLimit.result.decision, 'deny', 'Trusted invoice amount must remain authoritative and deny the $8,400 invoice.'); +assertEqual(overLimit.event.request.input?.amount, 8400); +assertEqual(overLimit.event.request.input?.invoiceId, 'INV-1043'); +assertEqual(overLimit.event.request.input?.vendorId, 'V-021'); + +const wrongDestination = authority.evaluate({ agentId, resource: 'payments', action: 'create', input: { currency: 'USD', amount: 400, destination: 'payments:attacker' } }); +assertEqual(wrongDestination.result.decision, 'deny', 'An alternate destination must not inherit payment authority.'); + +assertEqual(review.event.actionDigest.length, 64); +assertEqual(overLimit.event.actionDigest.length, 64); + +console.log('✓ authority boundary tests passed'); +console.log('✓ trusted invoice binding test passed'); +console.log('✓ destination substitution test passed'); diff --git a/hackathon/authority-ops/tsconfig.demo.json b/hackathon/authority-ops/tsconfig.demo.json new file mode 100644 index 0000000..8824ae1 --- /dev/null +++ b/hackathon/authority-ops/tsconfig.demo.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "outDir": "dist-demo", + "rootDir": "src" + }, + "include": [ + "src/authority.ts", + "src/domain.ts", + "src/ledger.ts", + "src/policy.ts", + "src/demo.ts", + "src/test.ts", + "src/node-shims.d.ts" + ] +} diff --git a/hackathon/authority-ops/tsconfig.json b/hackathon/authority-ops/tsconfig.json new file mode 100644 index 0000000..9a99a51 --- /dev/null +++ b/hackathon/authority-ops/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "outDir": "dist", + "rootDir": "src", + "declaration": true, + "sourceMap": true + }, + "include": ["src/**/*.ts"] +} diff --git a/hackathon/authority-ops/web/index.html b/hackathon/authority-ops/web/index.html new file mode 100644 index 0000000..93e9051 --- /dev/null +++ b/hackathon/authority-ops/web/index.html @@ -0,0 +1,147 @@ + + + + + + Authority Ops + + + +
+
+
+ Authority Ops +
+

Authority Ops

+
Professional invoice agent • explicit delegated authority
+
+
+
Authority Score100/100Time-bounded • constrained
+
+ +
+
Autonomous$0–$500 → ALLOW
+
Human review$501–$1,000 → ASK
+
Outside authority> $1,000 → DENY
+
+ +
+
+

Invoice queue

+
+ + + +
+
Select a case to inspect the proposed action, gate decision, and evidence.
+
+ +
+
+ +
+

Evidence chain

Receipt lifecycle
+
REQUESTEVIDENCEPROPOSALAUTHORIZATIONAPPROVALEXECUTION ATTEMPTDESTINATIONOUTCOME
+

+    
+
+ + +