a tool to help your Letta AI agents navigate bluesky

Merge pull request #3 from taurean/claude/research-atprotocol-schemas-01EeuX5nj55JCak34XjJACxM

added script for publishing autonomy schema

authored by taurean.bryant.land and committed by GitHub 13671142 9f4a64e9

+5
.env.example
··· 6 6 RESPONSIBLE_PARTY_NAME= 7 7 RESPONSIBLE_PARTY_CONTACT="example@example.com, example.com/contact, or @example.bsky.app" 8 8 9 + # Schema Publisher Credentials (ONLY for voyager.studio domain owner) 10 + # Template users do NOT need these - the schema is already published 11 + # SCHEMA_PUBLISHER_USERNAME= 12 + # SCHEMA_PUBLISHER_PASSWORD= 13 + 9 14 # AUTOMATION_LEVEL="automated" 10 15 # BSKY_SERVICE_URL=https://bsky.social 11 16 # BSKY_NOTIFICATION_TYPES="mention, reply"
+16
README.md
··· 24 24 - agent can be configured to _perform_: likes, posts, replies, reposts, quotes, following, blocking, muting (or undoing many of those) 25 25 - agent can also search bluesky and mute specific posts/threads to disengage 26 26 27 + ## AI transparency declaration 28 + 29 + This template automatically creates an **autonomy declaration record** in your agent's Bluesky PDS using the `studio.voyager.account.autonomy` schema. This is a standardized way for AI agents to transparently declare: 30 + 31 + - Their level of automation 32 + - Use of generative AI 33 + - Who is responsible for the account 34 + - What external services are being used 35 + 36 + **How it works:** 37 + - The **schema** is published once by voyager.studio (the template maintainer) 38 + - Your agent creates its own **record** using this schema when you run `deno task mount` 39 + - The record lives in your agent's PDS and is discoverable by other AT Protocol services 40 + 41 + This promotes transparency and accountability for AI agents on Bluesky. For more details, see [SCHEMA.md](./SCHEMA.md). 42 + 27 43 ## configurable variables: 28 44 ### required 29 45 - **`LETTA_API_KEY`**: your [letta API key](https://app.letta.com/api-keys).
+144
SCHEMA.md
··· 1 + # AI Agent Autonomy Declaration Schema 2 + 3 + ## Overview 4 + 5 + The `studio.voyager.account.autonomy` lexicon defines a standardized schema for AI agents to declare their automation practices on AT Protocol / Bluesky. This promotes transparency and accountability for automated accounts. 6 + 7 + ## Schema Details 8 + 9 + - **NSID**: `studio.voyager.account.autonomy` 10 + - **Type**: Record 11 + - **Authority**: `account.voyager.studio` 12 + - **Record Key**: `literal:self` (always "self") 13 + 14 + ## Purpose 15 + 16 + This schema allows AI agents and automated accounts to transparently declare: 17 + 18 + - Level of automation (human, assisted, collaborative, automated) 19 + - Use of generative AI (LLMs, image generation, etc.) 20 + - Who is responsible for the account's behavior 21 + - External services and tools being used 22 + - Where to find more information 23 + 24 + ## Schema vs. Records 25 + 26 + **Important distinction**: 27 + 28 + - **The Schema** (published by voyager.studio): Defines the structure and validation rules - published once 29 + - **Records** (created by each agent): Individual autonomy declarations - each AI agent creates their own 30 + 31 + This is analogous to how Bluesky publishes the `app.bsky.feed.post` schema once, but millions of users create their own post records. 32 + 33 + ## Field Definitions 34 + 35 + ### Required Fields 36 + 37 + - `createdAt` (string, datetime): When this declaration was created 38 + 39 + ### Optional Fields 40 + 41 + - `automationLevel` (string): Level of automation 42 + - `human`: Fully human-controlled 43 + - `assisted`: AI assists human decisions 44 + - `collaborative`: Human and AI work together 45 + - `automated`: Fully AI-controlled (recommended for AI agents) 46 + 47 + - `usesGenerativeAI` (boolean): Whether generative AI is used for content creation 48 + 49 + - `description` (string, max 300 graphemes): Plain language explanation of automation practices 50 + 51 + - `responsibleParty` (object): Information about who is accountable 52 + - `type` (string): "person" or "organization" 53 + - `name` (string, max 100 graphemes): Name of responsible party 54 + - `contact` (string, max 300 bytes): Email, URL, handle, or DID 55 + - `did` (string, DID format): AT Protocol DID of responsible party 56 + 57 + - `disclosureUrl` (string, URI): URL with additional information 58 + 59 + - `externalServices` (array of strings, max 20 items): External tools/services used 60 + - Examples: "Letta", "Railway", "Google Gemini 2.5-pro" 61 + 62 + ## Example Record 63 + 64 + ```json 65 + { 66 + "$type": "studio.voyager.account.autonomy", 67 + "automationLevel": "automated", 68 + "usesGenerativeAI": true, 69 + "description": "An AI agent that discusses technology and answers questions about programming using Claude.", 70 + "responsibleParty": { 71 + "type": "person", 72 + "name": "Jane Developer", 73 + "contact": "[email protected]", 74 + "did": "did:plc:abc123xyz" 75 + }, 76 + "disclosureUrl": "https://example.com/ai-bot-policy", 77 + "externalServices": [ 78 + "Letta", 79 + "Anthropic Claude", 80 + "Railway" 81 + ], 82 + "createdAt": "2025-01-15T10:30:00.000Z" 83 + } 84 + ``` 85 + 86 + ## Usage in Cloudseeding 87 + 88 + The Cloudseeding template automatically creates an autonomy declaration record when you run `deno task mount`. Configuration is done via environment variables in `.env`: 89 + 90 + - `AUTOMATION_LEVEL`: "assisted", "collaborative", or "automated" (default) 91 + - `PROJECT_DESCRIPTION`: Maps to `description` field 92 + - `RESPONSIBLE_PARTY_NAME`: Required 93 + - `RESPONSIBLE_PARTY_CONTACT`: Required 94 + - `RESPONSIBLE_PARTY_TYPE`: "person" or "organization" 95 + - `RESPONSIBLE_PARTY_BSKY`: DID or handle of responsible party 96 + - `DISCLOSURE_URL`: Optional URL to policy/disclosure page 97 + - `EXTERNAL_SERVICES`: Comma-separated list of external services 98 + 99 + ## Schema Evolution 100 + 101 + Following AT Protocol lexicon rules: 102 + 103 + - ✅ New optional fields can be added 104 + - ✅ Existing constraints can be tightened 105 + - ❌ Required fields cannot be removed 106 + - ❌ Field types cannot change 107 + - ❌ Constraints cannot be loosened 108 + 109 + Breaking changes require a new NSID (e.g., `studio.voyager.account.autonomy.v2`). 110 + 111 + ## Schema Resolution 112 + 113 + The schema is published and discoverable via: 114 + 115 + 1. **DNS TXT Record**: `_lexicon.account.voyager.studio` → DID of schema publisher 116 + 2. **PDS Record**: `com.atproto.lexicon.schema` collection in publisher's repository 117 + 3. **AT-URI**: `at://did:plc:*/com.atproto.lexicon.schema/studio.voyager.account.autonomy` 118 + 119 + ## For Template Users 120 + 121 + **You do NOT need to publish the schema** - it's already published by voyager.studio. You only need to: 122 + 123 + 1. Configure your `.env` file with your information 124 + 2. Run `deno task mount` to create YOUR autonomy declaration record 125 + 3. Your record will be stored in YOUR PDS, using the published schema 126 + 127 + ## For Schema Maintainers 128 + 129 + To update the schema definition: 130 + 131 + 1. Update `utils/declaration.ts` with schema changes 132 + 2. Ensure changes follow evolution rules 133 + 3. Run `deno task publish-schema` to publish updates 134 + 4. Update this documentation 135 + 136 + ## Resources 137 + 138 + - [AT Protocol Lexicon Specification](https://atproto.com/specs/lexicon) 139 + - [AT Protocol NSID Specification](https://atproto.com/specs/nsid) 140 + - [Cloudseeding Template Repository](https://github.com/taurean/cloudseeding) 141 + 142 + ## Contributing 143 + 144 + If you have suggestions for improving this schema, please open an issue or pull request in the Cloudseeding repository. Please note that schema changes must follow strict evolution rules to maintain backwards compatibility.
+2 -1
deno.json
··· 3 3 "config": "deno run --allow-read --allow-write setup.ts", 4 4 "mount": "deno run --allow-net --allow-env --allow-read --env mount.ts", 5 5 "watch": "deno run --allow-net --allow-env --env --watch main.ts", 6 - "start": "deno run --allow-net --allow-env --env main.ts" 6 + "start": "deno run --allow-net --allow-env --env main.ts", 7 + "publish-schema": "deno run --allow-net --allow-env --env scripts/publishSchema.ts" 7 8 }, 8 9 "imports": { 9 10 "@std/assert": "jsr:@std/assert@1",
+4
mount.ts
··· 24 24 import { searchingBlueskyMemory } from "./memories/searchingBluesky.ts"; 25 25 import { toolUseMemory } from "./memories/toolUse.ts"; 26 26 27 + // Submit autonomy declaration record to the agent's PDS for transparency 28 + // This uses the studio.voyager.account.autonomy schema published by voyager.studio 29 + console.log("📋 Creating AI autonomy declaration record..."); 27 30 await submitAutonomyDeclarationRecord(); 31 + console.log(""); 28 32 29 33 /** 30 34 * Core memory blocks that are ALWAYS attached to the agent.
+110
scripts/README.md
··· 1 + # Scripts 2 + 3 + This directory contains maintenance scripts for the Cloudseeding project. 4 + 5 + ## publishSchema.ts 6 + 7 + **Purpose**: Publishes the `studio.voyager.account.autonomy` lexicon schema to AT Protocol. 8 + 9 + **Who should run this**: Only the voyager.studio domain owner (project maintainer). 10 + 11 + **When to run this**: 12 + - Initial schema publication 13 + - When the schema definition is updated in `utils/declaration.ts` 14 + 15 + **Template users do NOT need to run this script.** The schema is already published and discoverable. Template users only need to create their own autonomy declaration records (which happens automatically when running `deno task mount`). 16 + 17 + ### Prerequisites 18 + 19 + 1. Control of the `voyager.studio` domain 20 + 2. DNS TXT record set up: 21 + ``` 22 + Name: _lexicon.account.voyager.studio 23 + Type: TXT 24 + Value: did=did:plc:YOUR_DID 25 + ``` 26 + 3. Credentials for a Bluesky account with a handle containing `voyager.studio` 27 + 28 + ### Usage 29 + 30 + **Option 1: Environment variables (recommended)** 31 + 32 + ```bash 33 + SCHEMA_PUBLISHER_USERNAME=your.voyager.studio.handle \ 34 + SCHEMA_PUBLISHER_PASSWORD=your-app-password \ 35 + deno task publish-schema 36 + ``` 37 + 38 + **Option 2: Add to .env file** 39 + 40 + Add these lines to `.env` (separate from your AI agent credentials): 41 + ```bash 42 + SCHEMA_PUBLISHER_USERNAME=your.voyager.studio.handle 43 + SCHEMA_PUBLISHER_PASSWORD=your-app-password 44 + ``` 45 + 46 + Then run: 47 + ```bash 48 + deno task publish-schema 49 + ``` 50 + 51 + **Note**: The script will verify that the logged-in account's handle contains `voyager.studio` before publishing. 52 + 53 + ### What it does 54 + 55 + 1. Logs into the voyager.studio account 56 + 2. Creates/updates a record in the `com.atproto.lexicon.schema` collection 57 + 3. Uses record key: `studio.voyager.account.autonomy` 58 + 4. Makes the schema discoverable by AT Protocol resolvers 59 + 60 + ### Schema vs. Records 61 + 62 + Understanding the distinction is important: 63 + 64 + | Schema | Records | 65 + |--------|---------| 66 + | Published once by domain owner | Created by each agent | 67 + | Defines structure and rules | Actual autonomy declarations | 68 + | Lives in voyager.studio PDS | Live in each agent's PDS | 69 + | One canonical definition | Many individual records | 70 + 71 + This is analogous to how Bluesky publishes the `app.bsky.feed.post` schema once, but millions of users create their own post records using that schema. 72 + 73 + ### Verification 74 + 75 + After publishing, you can verify: 76 + 77 + 1. **DNS Resolution**: 78 + ```bash 79 + dig TXT _lexicon.account.voyager.studio 80 + ``` 81 + 82 + 2. **Schema Record**: 83 + Visit your account's AT-URI: 84 + ``` 85 + at://YOUR_DID/com.atproto.lexicon.schema/studio.voyager.account.autonomy 86 + ``` 87 + 88 + 3. **PDS API** (if using bsky.social): 89 + ``` 90 + https://bsky.social/xrpc/com.atproto.repo.getRecord?repo=YOUR_DID&collection=com.atproto.lexicon.schema&rkey=studio.voyager.account.autonomy 91 + ``` 92 + 93 + ### Schema Evolution Rules 94 + 95 + When updating the schema, follow AT Protocol lexicon evolution rules: 96 + 97 + ✅ **Allowed**: 98 + - Adding new optional fields 99 + - Tightening existing constraints 100 + - Adding to `knownValues` arrays 101 + 102 + ❌ **Not Allowed**: 103 + - Removing required fields 104 + - Changing field types 105 + - Loosening constraints 106 + - Renaming fields 107 + 108 + Breaking changes require a new NSID (e.g., `studio.voyager.account.autonomy.v2`). 109 + 110 + See [SCHEMA.md](../SCHEMA.md) for complete schema documentation.
+139
scripts/publishSchema.ts
··· 1 + /** 2 + * Schema Publication Script 3 + * 4 + * This script publishes the studio.voyager.account.autonomy lexicon schema 5 + * to the voyager.studio PDS. This should only be run by the domain owner 6 + * to publish or update the canonical schema definition. 7 + * 8 + * Template users do NOT need to run this script - they just create records 9 + * using the published schema. 10 + * 11 + * Prerequisites: 12 + * 1. DNS TXT record: _lexicon.account.voyager.studio pointing to your DID 13 + * 2. SCHEMA_PUBLISHER_USERNAME: Bluesky handle for voyager.studio domain owner 14 + * 3. SCHEMA_PUBLISHER_PASSWORD: App password for that account 15 + * 16 + * Usage: 17 + * SCHEMA_PUBLISHER_USERNAME=your.handle SCHEMA_PUBLISHER_PASSWORD=xxxx deno task publish-schema 18 + * 19 + * OR set these in .env (separate from your agent credentials): 20 + * SCHEMA_PUBLISHER_USERNAME=... 21 + * SCHEMA_PUBLISHER_PASSWORD=... 22 + */ 23 + 24 + import { AtpAgent } from "@atproto/api"; 25 + import { AUTONOMY_DECLARATION_LEXICON } from "../utils/declaration.ts"; 26 + 27 + const publishSchema = async () => { 28 + console.log("🔧 Publishing studio.voyager.account.autonomy schema...\n"); 29 + 30 + // Get credentials from environment - use separate vars from agent credentials 31 + const username = Deno.env.get("SCHEMA_PUBLISHER_USERNAME") || Deno.env.get("BSKY_USERNAME"); 32 + const password = Deno.env.get("SCHEMA_PUBLISHER_PASSWORD") || Deno.env.get("BSKY_APP_PASSWORD"); 33 + const serviceUrl = Deno.env.get("BSKY_SERVICE_URL") || "https://bsky.social"; 34 + const expectedDomain = Deno.env.get("SCHEMA_DOMAIN") || "voyager.studio"; 35 + 36 + if (!username || !password) { 37 + console.error( 38 + "❌ Error: Schema publisher credentials not found.\n" 39 + ); 40 + console.error(" Set SCHEMA_PUBLISHER_USERNAME and SCHEMA_PUBLISHER_PASSWORD"); 41 + console.error(" OR run with: SCHEMA_PUBLISHER_USERNAME=your.handle SCHEMA_PUBLISHER_PASSWORD=xxxx deno task publish-schema\n"); 42 + console.error(" ⚠️ Template users: You do NOT need to run this command!"); 43 + console.error(" The schema is already published by voyager.studio.\n"); 44 + Deno.exit(1); 45 + } 46 + 47 + // Initialize agent 48 + const agent = new AtpAgent({ service: serviceUrl }); 49 + 50 + try { 51 + // Login 52 + console.log("🔐 Logging in..."); 53 + await agent.login({ 54 + identifier: username, 55 + password: password, 56 + }); 57 + console.log(`✅ Logged in as: ${agent.session?.handle} (${agent.session?.did})\n`); 58 + 59 + // Verify domain ownership 60 + const handle = agent.session?.handle || ""; 61 + if (!handle.includes(expectedDomain)) { 62 + console.error(`❌ Error: This account does not appear to own ${expectedDomain}\n`); 63 + console.error(` Logged in as: ${handle}`); 64 + console.error(` Expected domain: ${expectedDomain}\n`); 65 + console.error(" ⚠️ Template users: You do NOT need to publish the schema!"); 66 + console.error(" The schema is already published and discoverable."); 67 + console.error(" You only create your own autonomy declaration record via 'deno task mount'.\n"); 68 + Deno.exit(1); 69 + } 70 + 71 + // Prepare schema record 72 + const schemaRecord = { 73 + $type: "com.atproto.lexicon.schema", 74 + lexicon: AUTONOMY_DECLARATION_LEXICON.lexicon, 75 + id: AUTONOMY_DECLARATION_LEXICON.id, 76 + defs: AUTONOMY_DECLARATION_LEXICON.defs, 77 + description: 78 + "Lexicon for declaring AI agent autonomy and automation practices for transparency and accountability", 79 + }; 80 + 81 + console.log(`📋 Schema Details:`); 82 + console.log(` NSID: ${schemaRecord.id}`); 83 + console.log(` Lexicon Version: ${schemaRecord.lexicon}`); 84 + console.log(` Definitions: ${Object.keys(schemaRecord.defs).join(", ")}\n`); 85 + 86 + // Check if schema already exists 87 + let existingSchema = null; 88 + try { 89 + const existing = await agent.com.atproto.repo.getRecord({ 90 + repo: agent.session?.did!, 91 + collection: "com.atproto.lexicon.schema", 92 + rkey: "studio.voyager.account.autonomy", 93 + }); 94 + existingSchema = existing.data; 95 + console.log("📄 Existing schema found - will update\n"); 96 + } catch (error: any) { 97 + if (error?.status === 400 || error?.status === 404) { 98 + console.log("📄 No existing schema found - will create new\n"); 99 + } else { 100 + throw error; 101 + } 102 + } 103 + 104 + // Publish schema 105 + console.log("📤 Publishing schema..."); 106 + const result = await agent.com.atproto.repo.putRecord({ 107 + repo: agent.session?.did!, 108 + collection: "com.atproto.lexicon.schema", 109 + rkey: "studio.voyager.account.autonomy", 110 + record: schemaRecord, 111 + }); 112 + 113 + console.log(`✅ Schema ${existingSchema ? "updated" : "published"} successfully!\n`); 114 + console.log(`📍 AT-URI: ${result.uri}`); 115 + console.log(`🔗 CID: ${result.cid}\n`); 116 + 117 + // Verify DNS setup 118 + console.log("🔍 Next steps:"); 119 + console.log(" 1. Verify DNS TXT record is set:"); 120 + console.log(" Name: _lexicon.account.voyager.studio"); 121 + console.log(" Type: TXT"); 122 + console.log(` Value: did=${agent.session?.did}`); 123 + console.log( 124 + "\n 2. Test resolution (may take time for DNS to propagate):", 125 + ); 126 + console.log( 127 + ` dig TXT _lexicon.account.voyager.studio\n`, 128 + ); 129 + console.log("✨ Schema is now discoverable by AT Protocol resolvers!"); 130 + } catch (error) { 131 + console.error("❌ Error publishing schema:", error); 132 + Deno.exit(1); 133 + } 134 + }; 135 + 136 + // Run if executed directly 137 + if (import.meta.main) { 138 + publishSchema(); 139 + }
+18
utils/declaration.ts
··· 3 3 import { Lexicons } from "@atproto/lexicon"; 4 4 import { agentContext } from "./agentContext.ts"; 5 5 6 + /** 7 + * Autonomy Declaration Lexicon Schema 8 + * 9 + * This defines the canonical schema for AI agent autonomy declarations 10 + * in AT Protocol. The schema is published at voyager.studio and is 11 + * used by all Cloudseeding template instances. 12 + * 13 + * Schema vs. Records: 14 + * - The SCHEMA is published once by voyager.studio (domain owner) 15 + * - Each agent creates their own RECORD using this schema 16 + * 17 + * Template users do NOT need to publish this schema - it's already 18 + * published and discoverable via DNS resolution. They only need to 19 + * create their own autonomy declaration record (done automatically 20 + * by submitAutonomyDeclarationRecord below). 21 + * 22 + * See SCHEMA.md for full documentation. 23 + */ 6 24 export const AUTONOMY_DECLARATION_LEXICON = { 7 25 "lexicon": 1, 8 26 "id": "studio.voyager.account.autonomy",