From 0734174dbfa3b72786f5867ff65f51b9720d9c59 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:19:36 -0500 Subject: [PATCH 01/27] Add Authority Ops TypeScript config --- hackathon/authority-ops/tsconfig.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 hackathon/authority-ops/tsconfig.json 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"] +} From 70c887b742478625b1e2fd3c36d383eaa3e4e3fc Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:19:40 -0500 Subject: [PATCH 02/27] Add Authority Ops demo build config --- hackathon/authority-ops/tsconfig.demo.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 hackathon/authority-ops/tsconfig.demo.json 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" + ] +} From 5657ddc5f3f33950510e8332c3b8c042b64a4ea7 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:19:45 -0500 Subject: [PATCH 03/27] Add Authority Ops hackathon disclosure --- hackathon/authority-ops/HACKATHON.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 hackathon/authority-ops/HACKATHON.md 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. From cc6c7722bfd3c6226bc58b10d3d130058948ae16 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:19:52 -0500 Subject: [PATCH 04/27] Add Authority Ops README --- hackathon/authority-ops/README.md | 97 +++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 hackathon/authority-ops/README.md diff --git a/hackathon/authority-ops/README.md b/hackathon/authority-ops/README.md new file mode 100644 index 0000000..10d26ca --- /dev/null +++ b/hackathon/authority-ops/README.md @@ -0,0 +1,97 @@ +# 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**: up to $500 can run autonomously; $501–$1,000 requires review; above $1,000 is denied. + +| Invoice | Amount | Authority result | What happens | +| --- | ---: | --- | --- | +| INV-1041 | $480 | ALLOW | Simulated payment is submitted | +| INV-1042 | $800 | ASK | The payment request waits for human approval | +| 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 + │ + └────────────┬─────────────┘ + ▼ + 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 From dc609979257628d8bbcf6fe91e95253d5d1bc77c Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:19:57 -0500 Subject: [PATCH 05/27] Add Authority Ops domain types --- hackathon/authority-ops/src/domain.ts | 58 +++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 hackathon/authority-ops/src/domain.ts 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'; +} From 70d25ccc7ab1fd81453b0579a4e4eeb71302b22c Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:20:05 -0500 Subject: [PATCH 06/27] Add Authority Ops authority adapter --- hackathon/authority-ops/src/authority.ts | 95 ++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 hackathon/authority-ops/src/authority.ts diff --git a/hackathon/authority-ops/src/authority.ts b/hackathon/authority-ops/src/authority.ts new file mode 100644 index 0000000..edc324f --- /dev/null +++ b/hackathon/authority-ops/src/authority.ts @@ -0,0 +1,95 @@ +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): 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) }, decision: 'allow' }; + const review: Capability = { resource: 'payments', actions: ['create'], constraints: { currency: 'USD', amount_lte: maxPayment }, 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] + }; +} From a0dd3263cc360076f0ee7eb81efb258440a03d23 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:20:12 -0500 Subject: [PATCH 07/27] Add Authority Ops ledger --- hackathon/authority-ops/src/ledger.ts | 43 +++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 hackathon/authority-ops/src/ledger.ts 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}`); +} From 90a73da5b9417ee4a3f4785984924e9416889b46 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:20:17 -0500 Subject: [PATCH 08/27] Add Authority Ops invoice policy --- hackathon/authority-ops/src/policy.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 hackathon/authority-ops/src/policy.ts 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.' }; +} From 5868f78bb391e4aa8468b1d6e2b148f263f15e0d Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:20:25 -0500 Subject: [PATCH 09/27] Add Authority Ops deterministic demo --- hackathon/authority-ops/src/demo.ts | 65 +++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 hackathon/authority-ops/src/demo.ts diff --git a/hackathon/authority-ops/src/demo.ts b/hackathon/authority-ops/src/demo.ts new file mode 100644 index 0000000..4f3da0a --- /dev/null +++ b/hackathon/authority-ops/src/demo.ts @@ -0,0 +1,65 @@ +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' }, +]; + +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'); + +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-1041', 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: 'payments:demo-ledger' } }); + extras.push({ type: 'outcome', at: new Date().toISOString(), payload: { status: 'completed', paymentId: 'PAY-DEMO-1041' } }); + } else if (result.decision === 'ask') { + console.log('Execution: waiting for human approval.'); + extras.push({ type: 'approval', at: new Date().toISOString(), payload: { status: 'pending', reason: 'Human approval required by authority policy.' } }); + extras.push({ type: 'outcome', at: new Date().toISOString(), payload: { status: 'approval_required' } }); + } 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: 'outcome', at: new Date().toISOString(), payload: { status: 'blocked', reason: result.reasons[0] } }); + } + + printReceipt(makeReceipt(invoice, event, tools, extras)); +} From 38fa98fa7d6a975c494da9f94f64689050f73a1c Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:20:30 -0500 Subject: [PATCH 10/27] Add Authority Ops authority tests --- hackathon/authority-ops/src/test.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 hackathon/authority-ops/src/test.ts diff --git a/hackathon/authority-ops/src/test.ts b/hackathon/authority-ops/src/test.ts new file mode 100644 index 0000000..4bc77de --- /dev/null +++ b/hackathon/authority-ops/src/test.ts @@ -0,0 +1,27 @@ +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 type { AgentPassport } from './domain.js'; + +const agentId = 'agent_test'; +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)); + +const low = authority.evaluate({ agentId, resource: 'payments', action: 'create', input: { currency: 'USD', amount: 500 } }); +assertEqual(low.result.decision, 'allow', 'Autonomous tier should allow a payment at or below $500.'); + +const review = authority.evaluate({ agentId, resource: 'payments', action: 'create', input: { currency: 'USD', amount: 800 } }); +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 } }); +assertEqual(wrongAgent.result.decision, 'deny'); + +const overLimit = authority.evaluate({ agentId, resource: 'payments', action: 'create', input: { currency: 'USD', amount: 8400 } }); +assertEqual(overLimit.result.decision, 'deny'); + +console.log('✓ authority boundary tests passed'); From aa500fed48f8197689e0890b2fd4285b73ce5d45 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:20:34 -0500 Subject: [PATCH 11/27] Add Authority Ops demo type shim --- hackathon/authority-ops/src/node-shims.d.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 hackathon/authority-ops/src/node-shims.d.ts 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; +} From 0a1ca4b0c41b10e6e973a68d36ea6b8640c12f3f Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:20:41 -0500 Subject: [PATCH 12/27] Add live Strands agent integration --- hackathon/authority-ops/src/live-agent.ts | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 hackathon/authority-ops/src/live-agent.ts diff --git a/hackathon/authority-ops/src/live-agent.ts b/hackathon/authority-ops/src/live-agent.ts new file mode 100644 index 0000000..007d95e --- /dev/null +++ b/hackathon/authority-ops/src/live-agent.ts @@ -0,0 +1,69 @@ +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 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); +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 a payment. This tool MUST call the authority layer before any simulated payment is created.', + inputSchema: z.object({ invoiceId: z.string(), amount: z.number(), vendorId: z.string(), currency: z.string() }), + callback: ({ invoiceId, amount, vendorId, currency }) => { + const invoice = invoices.find((x) => x.invoiceId === invoiceId); + if (!invoice) throw new Error(`Unknown invoice ${invoiceId}`); + const { result, event } = authority.evaluate({ agentId, resource: 'payments', action: 'create', input: { currency, amount } }); + return { + invoiceId, vendorId, amount, currency, decision: result.decision, reasons: result.reasons, + authorityEventId: event.eventId, 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.', + 'Your job is to inspect invoices and propose or request payment actions.', + 'Never treat your own proposal as authorization.', + 'Payment execution is only possible through request_payment, which is policy-controlled.', + 'For a user-selected invoice, explain the proposal, the authority decision, and the resulting 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); From 03bae68b9051def6be94e1c39545332d05e5162f Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:20:46 -0500 Subject: [PATCH 13/27] Add Authority Ops architecture document --- hackathon/authority-ops/docs/ARCHITECTURE.md | 41 ++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 hackathon/authority-ops/docs/ARCHITECTURE.md 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 From 8ec98a0aea9b7b9944e38f92beb9e3132456f2b5 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:20:51 -0500 Subject: [PATCH 14/27] Add Authority Ops invoice scenarios --- .../authority-ops/scenarios/invoices.json | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 hackathon/authority-ops/scenarios/invoices.json 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" + } +] From 8e2dd36f09257453c260da1d903c18ebe03203d4 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:21:05 -0500 Subject: [PATCH 15/27] Add Authority Ops demo UI --- hackathon/authority-ops/web/index.html | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 hackathon/authority-ops/web/index.html diff --git a/hackathon/authority-ops/web/index.html b/hackathon/authority-ops/web/index.html new file mode 100644 index 0000000..fd37ccb --- /dev/null +++ b/hackathon/authority-ops/web/index.html @@ -0,0 +1,70 @@ + + + + + + Authority Ops + + + +
+
+

Authority Ops

Professional invoice agent • explicit delegated authority
+
Authority Score100/100Time-bounded • constrained
+
+
+

Invoice queue

+ + + +
+
Select an invoice

Authority decision

Proposal is not authorization. Authorization is not execution. Execution is not outcome.

+
+

Evidence chain

+
REQUESTEVIDENCEPROPOSALAUTHORIZATIONAPPROVALEXECUTION ATTEMPTDESTINATIONOUTCOME
+
Select an invoice to inspect the ledger receipt.
+
+
+ + + From f91bc34f85832b1aece43e776af238d5c2bb8c1d Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:21:13 -0500 Subject: [PATCH 16/27] Add Authority Ops architecture diagram --- .../authority-ops/assets/architecture.svg | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 hackathon/authority-ops/assets/architecture.svg 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 + From ecc889f77fef0835d074415eec52906fe820e2a7 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:27:59 -0500 Subject: [PATCH 17/27] Fix Authority Ops authority tiers and approval demo --- hackathon/authority-ops/README.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/hackathon/authority-ops/README.md b/hackathon/authority-ops/README.md index 10d26ca..61e4911 100644 --- a/hackathon/authority-ops/README.md +++ b/hackathon/authority-ops/README.md @@ -12,12 +12,16 @@ 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**: up to $500 can run autonomously; $501–$1,000 requires review; above $1,000 is denied. +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 | The payment request waits for human approval | +| 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. @@ -43,10 +47,16 @@ The intentionally repetitive workflow makes the authority boundary visible inste │ │ │ ▼ ▼ ▼ execute approval blocked - │ - └────────────┬─────────────┘ - ▼ - Ledger receipt + │ │ + │ ▼ + │ approve + │ │ + └──────┬─────┘ + ▼ + execute + │ + ▼ + Ledger receipt Ledger chain: REQUEST → EVIDENCE → PROPOSAL → AUTHORIZATION → From 7294e8716c18bc1985f7f4fa3e3fe1d8c759d50d Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:28:10 -0500 Subject: [PATCH 18/27] Complete human approval path in Authority Ops demo --- hackathon/authority-ops/src/demo.ts | 57 ++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/hackathon/authority-ops/src/demo.ts b/hackathon/authority-ops/src/demo.ts index 4f3da0a..149ee4e 100644 --- a/hackathon/authority-ops/src/demo.ts +++ b/hackathon/authority-ops/src/demo.ts @@ -1,3 +1,4 @@ +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'; @@ -17,10 +18,39 @@ const invoices: Invoice[] = [ { 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══════════════════════════════════════════'); @@ -47,17 +77,32 @@ for (const invoice of invoices) { if (result.decision === 'allow') { console.log('Execution: simulated payment submitted.'); - tools.push({ name: 'request_payment', input: proposal, output: { paymentId: 'PAY-DEMO-1041', status: 'submitted' }, resultStatus: 'success' }); + 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: 'payments:demo-ledger' } }); - extras.push({ type: 'outcome', at: new Date().toISOString(), payload: { status: 'completed', paymentId: 'PAY-DEMO-1041' } }); + 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('Execution: waiting for human approval.'); - extras.push({ type: 'approval', at: new Date().toISOString(), payload: { status: 'pending', reason: 'Human approval required by authority policy.' } }); - extras.push({ type: 'outcome', at: new Date().toISOString(), payload: { status: 'approval_required' } }); + 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] } }); } From d1ecfeb138804eb16636dea80acfecb590ca2675 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:28:16 -0500 Subject: [PATCH 19/27] Strengthen Authority Ops approval boundary tests --- hackathon/authority-ops/src/test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hackathon/authority-ops/src/test.ts b/hackathon/authority-ops/src/test.ts index 4bc77de..9a115d2 100644 --- a/hackathon/authority-ops/src/test.ts +++ b/hackathon/authority-ops/src/test.ts @@ -24,4 +24,8 @@ assertEqual(wrongAgent.result.decision, 'deny'); const overLimit = authority.evaluate({ agentId, resource: 'payments', action: 'create', input: { currency: 'USD', amount: 8400 } }); assertEqual(overLimit.result.decision, 'deny'); +// The human-review boundary must remain bounded even after approval. +assertEqual(review.event.request.input?.amount, 800); +assertEqual(review.event.actionDigest.length, 64); + console.log('✓ authority boundary tests passed'); From 980d7defc59cf593a353be48f17091d36d7d0a3b Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:28:44 -0500 Subject: [PATCH 20/27] Add Authority Ops package manifest --- hackathon/authority-ops/package.json | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 hackathon/authority-ops/package.json diff --git a/hackathon/authority-ops/package.json b/hackathon/authority-ops/package.json new file mode 100644 index 0000000..9c0b421 --- /dev/null +++ b/hackathon/authority-ops/package.json @@ -0,0 +1,23 @@ +{ + "name": "authority-ops", + "version": "0.1.0", + "private": true, + "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 .tmp/demo.js", + "test": "tsc -p tsconfig.demo.json && node .tmp/test.js", + "live": "tsc -p tsconfig.json && node dist/live-agent.js" + }, + "dependencies": { + "@strands-agents/sdk": "latest", + "zod": "^4.1.11" + }, + "devDependencies": { + "@types/node": "^24.3.0", + "typescript": "^5.9.2" + } +} From b45b3bbaab6eca3bd726e1687d588213b0d11a2c Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:29:08 -0500 Subject: [PATCH 21/27] Pin Authority Ops to current Strands 1.16 line --- hackathon/authority-ops/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hackathon/authority-ops/package.json b/hackathon/authority-ops/package.json index 9c0b421..8c12370 100644 --- a/hackathon/authority-ops/package.json +++ b/hackathon/authority-ops/package.json @@ -1,20 +1,20 @@ { "name": "authority-ops", "version": "0.1.0", - "private": true, + "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 .tmp/demo.js", - "test": "tsc -p tsconfig.demo.json && node .tmp/test.js", + "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": "latest", - "zod": "^4.1.11" + "@strands-agents/sdk": "^1.16.0", + "zod": "^4.1.5" }, "devDependencies": { "@types/node": "^24.3.0", From 41d29b35507d48541cbf924a3a8198abade95b88 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:29:17 -0500 Subject: [PATCH 22/27] Add Authority Ops CI validation --- .github/workflows/authority-ops.yml | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/authority-ops.yml 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 From c6dce564317ab9553eb538bedd7076b70627431b Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:42:58 -0500 Subject: [PATCH 23/27] Polish interactive Authority Ops demo UI --- hackathon/authority-ops/web/index.html | 149 +++++++++++++++++++------ 1 file changed, 113 insertions(+), 36 deletions(-) diff --git a/hackathon/authority-ops/web/index.html b/hackathon/authority-ops/web/index.html index fd37ccb..93e9051 100644 --- a/hackathon/authority-ops/web/index.html +++ b/hackathon/authority-ops/web/index.html @@ -5,65 +5,142 @@ Authority Ops
-

Authority Ops

Professional invoice agent • explicit delegated authority
-
Authority Score100/100Time-bounded • constrained
+
+ 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 an invoice

Authority decision

Proposal is not authorization. Authorization is not execution. Execution is not outcome.

+
+

Invoice queue

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

Evidence chain

+ +
+

Evidence chain

Receipt lifecycle
REQUESTEVIDENCEPROPOSALAUTHORIZATIONAPPROVALEXECUTION ATTEMPTDESTINATIONOUTCOME
-
Select an invoice to inspect the ledger receipt.
+

     
From aaad0ba1072022d025c39de27ea4ed49df6f54c4 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 15:54:54 -0500 Subject: [PATCH 24/27] Red-team harden payment authorization boundaries --- hackathon/authority-ops/src/authority.ts | 26 ++++++++++--- hackathon/authority-ops/src/live-agent.ts | 45 ++++++++++++++++++----- hackathon/authority-ops/src/payment.ts | 16 ++++++++ hackathon/authority-ops/src/test.ts | 42 +++++++++++++++------ 4 files changed, 103 insertions(+), 26 deletions(-) create mode 100644 hackathon/authority-ops/src/payment.ts diff --git a/hackathon/authority-ops/src/authority.ts b/hackathon/authority-ops/src/authority.ts index edc324f..5149c27 100644 --- a/hackathon/authority-ops/src/authority.ts +++ b/hackathon/authority-ops/src/authority.ts @@ -83,13 +83,29 @@ export function authorityScore(contract: AgentContract): number { return Math.max(0, Math.min(100, score)); } -export function buildContract(agentId: string, maxPayment: number): AgentContract { +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) }, decision: 'allow' }; - const review: Capability = { resource: 'payments', actions: ['create'], constraints: { currency: 'USD', amount_lte: maxPayment }, decision: 'ask' }; + 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] + 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/live-agent.ts b/hackathon/authority-ops/src/live-agent.ts index 007d95e..5b479a8 100644 --- a/hackathon/authority-ops/src/live-agent.ts +++ b/hackathon/authority-ops/src/live-agent.ts @@ -4,11 +4,12 @@ import { AgentAuthorityAdapter, authorityScore, buildContract } from './authorit 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); +const contract = buildContract(agentId, 1000, paymentDestination); const authority = new AgentAuthorityAdapter(passport, contract); const invoices: Invoice[] = [ @@ -30,15 +31,38 @@ const readInvoice = tool({ const requestPayment = tool({ name: 'request_payment', - description: 'Request a payment. This tool MUST call the authority layer before any simulated payment is created.', - inputSchema: z.object({ invoiceId: z.string(), amount: z.number(), vendorId: z.string(), currency: z.string() }), - callback: ({ invoiceId, amount, vendorId, currency }) => { + 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 { result, event } = authority.evaluate({ agentId, resource: 'payments', action: 'create', input: { currency, amount } }); + + 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, amount, currency, decision: result.decision, reasons: result.reasons, - authorityEventId: event.eventId, authorityScore: authorityScore(contract), executed: result.decision === 'allow', + 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', }; }, }); @@ -54,10 +78,11 @@ const agent = new Agent({ tools: [readInvoice, requestPayment], systemPrompt: [ 'You are Authority Ops, a professional accounts-payable operations agent.', - 'Your job is to inspect invoices and propose or request payment actions.', + 'Inspect invoices and request payment only through the request_payment tool.', 'Never treat your own proposal as authorization.', - 'Payment execution is only possible through request_payment, which is policy-controlled.', - 'For a user-selected invoice, explain the proposal, the authority decision, and the resulting outcome.', + '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(' '), }); 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/test.ts b/hackathon/authority-ops/src/test.ts index 9a115d2..e19d5d2 100644 --- a/hackathon/authority-ops/src/test.ts +++ b/hackathon/authority-ops/src/test.ts @@ -3,29 +3,49 @@ function assertEqual(actual: unknown, expected: unknown, message = 'assertion fa } import { AgentAuthorityAdapter, buildContract } from './authority.js'; -import type { AgentPassport } from './domain.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)); +const authority = new AgentAuthorityAdapter(passport, buildContract(agentId, 1000, paymentDestination)); -const low = authority.evaluate({ agentId, resource: 'payments', action: 'create', input: { currency: 'USD', amount: 500 } }); -assertEqual(low.result.decision, 'allow', 'Autonomous tier should allow a payment at or below $500.'); +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 review = authority.evaluate({ agentId, resource: 'payments', action: 'create', input: { currency: 'USD', amount: 800 } }); +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 } }); -assertEqual(wrongAgent.result.decision, 'deny'); +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 overLimit = authority.evaluate({ agentId, resource: 'payments', action: 'create', input: { currency: 'USD', amount: 8400 } }); -assertEqual(overLimit.result.decision, 'deny'); +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.'); -// The human-review boundary must remain bounded even after approval. -assertEqual(review.event.request.input?.amount, 800); 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'); From b22446e791f18e1fd49d2eb23245400c4c6db216 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 20:05:11 -0500 Subject: [PATCH 25/27] Add hackathon submission package and demo plan --- .../authority-ops/HACKATHON-SUBMISSION.md | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 hackathon/authority-ops/HACKATHON-SUBMISSION.md 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**. From 3332ec40ca6ebc7ec470f0d6a76c70d76ad3a0f9 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 20:05:23 -0500 Subject: [PATCH 26/27] Add AWS Builder blog draft for hackathon bonus --- .../authority-ops/AWS-BUILDER-BLOG-DRAFT.md | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 hackathon/authority-ops/AWS-BUILDER-BLOG-DRAFT.md 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. From ec7d5724fc758bccded503306b7b1026325a20a0 Mon Sep 17 00:00:00 2001 From: prayingperceptions Date: Tue, 8 Sep 2026 21:17:57 -0500 Subject: [PATCH 27/27] docs: add Authority Ops project overview and build stack --- hackathon/authority-ops/ABOUT-PROJECT.md | 62 ++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 hackathon/authority-ops/ABOUT-PROJECT.md 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