From 41273f0733166a3178e326fd3db95c794c9f1d15 Mon Sep 17 00:00:00 2001 From: tore Date: Thu, 28 May 2026 14:40:08 -0600 Subject: [PATCH] feat: agent sample with bearer authed tool call --- agents/README.md | 1 + agents/internal-caller-agent/.aaignore | 11 ++ agents/internal-caller-agent/README.md | 119 ++++++++++++++++++ agents/internal-caller-agent/agent.yaml | 53 ++++++++ agents/internal-caller-agent/env.example | 5 + agents/internal-caller-agent/sample.yaml | 11 ++ .../scripts/get-auth-status.aascript | 21 ++++ samples.json | 8 ++ 8 files changed, 229 insertions(+) create mode 100644 agents/internal-caller-agent/.aaignore create mode 100644 agents/internal-caller-agent/README.md create mode 100644 agents/internal-caller-agent/agent.yaml create mode 100644 agents/internal-caller-agent/env.example create mode 100644 agents/internal-caller-agent/sample.yaml create mode 100644 agents/internal-caller-agent/scripts/get-auth-status.aascript diff --git a/agents/README.md b/agents/README.md index e6d7ed8..3bebfd5 100644 --- a/agents/README.md +++ b/agents/README.md @@ -15,6 +15,7 @@ command installs the whole thing. | [compliance-reviewer](compliance-reviewer) | Reviews PRs against SOC2 / GDPR / your custom compliance rules | | [cross-org-collab-agent](cross-org-collab-agent) | Privacy by construction — multi-layer field guards for cross-org threads | | [fde-agent](fde-agent) | Forward Deployed Engineer agent — runs a discovery → implementation → validation → handoff playbook, specialized with your project knowledge | +| [internal-caller-agent](internal-caller-agent) | Demonstrates authenticated outbound API calls with an agent-scoped bearer token | | [onboarding-qa](onboarding-qa) | Answers new-hire questions from your knowledge base | | [platform-health-agent](platform-health-agent) | Daily report on repo health, GitHub event alerts, and a 5xx digest from production logs — delivered to Slack | | [release-notes-bot](release-notes-bot) | Watches merged PRs weekly, drafts changelog as a GitHub issue | diff --git a/agents/internal-caller-agent/.aaignore b/agents/internal-caller-agent/.aaignore new file mode 100644 index 0000000..e94987e --- /dev/null +++ b/agents/internal-caller-agent/.aaignore @@ -0,0 +1,11 @@ +# Auto-generated by `uv run scripts/sample_tool.py generate` — do not edit. +# +# This sample is installed via `archastro install agentsample `, +# which reads sample.yaml's `steps:` block and runs the DSL executor. +# `archastro deploy configs` should skip every file inside this +# directory. +README.md +agent.yaml +env.example +sample.yaml +scripts/ diff --git a/agents/internal-caller-agent/README.md b/agents/internal-caller-agent/README.md new file mode 100644 index 0000000..9c8bf0e --- /dev/null +++ b/agents/internal-caller-agent/README.md @@ -0,0 +1,119 @@ +# Internal Caller Agent + +## Deploy with your coding agent + +Paste this into Claude Code, Codex, or any AI coding assistant: + +``` +Deploy the Internal Caller Agent from this repo. + +1) Read agents/internal-caller-agent/agent.yaml and agents/internal-caller-agent/env.example +2) Install the ArchAgents CLI if missing: brew install ArchAstro/tools/archagent +3) Run: archagent auth login && archagent init +4) Deploy: archagent install agentsample internal-caller-agent +5) Create a demo bearer token: TOKEN="$(openssl rand -hex 32)" +6) Set it on the deployed agent: archagent create agentenvvar --agent --key AGENT_BEARER_TOKEN --value "$TOKEN" +7) Test it: create an agent session and ask it to call get_auth_status +8) Confirm the response says authenticated=true, status=200, token_was_echoed=true, and token_matched_secret=true +``` + +> Demonstrates outbound API authentication with an agent-scoped bearer token. + +This sample is intentionally small: one agent, one script-backed custom +tool, and one agent environment variable. The tool calls +`https://httpbin.org/bearer` with: + +```bash +curl -sS "https://httpbin.org/bearer" \ + -H "Authorization: Bearer $AGENT_BEARER_TOKEN" +``` + +The token is stored as an agent-scoped secret, so it is available to this +agent's server-side scripts without exposing it in the agent template or +tool arguments. + +## What it does + +When asked to check auth, the agent calls `get_auth_status`. The script: + +1. Reads `env.AGENT_BEARER_TOKEN` +2. Sends it as a bearer token in the `Authorization` header +3. Reads httpbin's response +4. Returns a safe summary: + - `authenticated` + - HTTP `status` + - `token_was_echoed` + - `token_matched_secret` + +It does not return the full bearer token. + +## Setup + +Deploy the sample: + +```bash +archagent install agentsample internal-caller-agent +``` + +Then create the agent-scoped secret. Use the deployed agent ID printed by +the install command: + +```bash +TOKEN="$(openssl rand -hex 32)" + +archagent create agentenvvar \ + --agent \ + --key AGENT_BEARER_TOKEN \ + --value "$TOKEN" \ + --description "Demo bearer token for authenticated outbound request sample" +``` + +## Test it + +Create a short session: + +```bash +archagent create agentsession \ + --agent \ + --instructions "Call get_auth_status once and report the result." \ + --wait +``` + +Expected result: + +```text +authenticated: true +status: 200 +token_was_echoed: true +token_matched_secret: true +``` + +## Why agent-scoped secrets + +Use agent-scoped environment variables when a credential belongs to one +agent's job. Compared with org-scoped variables, this keeps the blast +radius smaller: another agent in the same org cannot read this token just +because it can run scripts. + +For shared credentials, use an org-scoped env var instead and reference it +from multiple agents. + +## What this demonstrates + +- Script-backed custom tools +- Agent-scoped environment variables +- Bearer-token `Authorization` headers +- Server-side HTTP calls from an agent tool +- Avoiding secrets in tool inputs, templates, and chat transcripts + +## Files + +```text +internal-caller-agent/ +|-- README.md +|-- agent.yaml +|-- env.example +|-- sample.yaml +`-- scripts/ + `-- get-auth-status.aascript +``` diff --git a/agents/internal-caller-agent/agent.yaml b/agents/internal-caller-agent/agent.yaml new file mode 100644 index 0000000..7f45dfc --- /dev/null +++ b/agents/internal-caller-agent/agent.yaml @@ -0,0 +1,53 @@ +kind: AgentTemplate +agent_key: internal-caller-agent +name: Internal Caller Agent +model: openrouter/anthropic/claude-sonnet-latest +description: Demonstrates authenticated outbound API calls from an agent-scoped secret. +identity: | + You are Internal Caller Agent, a concise demo agent for authenticated + outbound API requests. + + When a user asks whether your bearer-token authentication is working, + call `get_auth_status` and report: + + - whether the request authenticated + - the HTTP status from httpbin + - whether the echoed token matched your agent-scoped secret + - whether httpbin echoed a token + + Keep the explanation short and make it clear that the bearer token came + from the agent-scoped `AGENT_BEARER_TOKEN` environment variable. Never + include the full bearer token or a partial token value in your response. + +tools: + - kind: custom + name: get_auth_status + description: | + Call httpbin's bearer-token endpoint with the agent-scoped + AGENT_BEARER_TOKEN secret and return the auth result. + parameters: + type: object + properties: {} + required: [] + handler_type: script + config_ref: get-auth-status + status: active + +routines: + - name: Participate in conversations + description: Join conversations and respond to user messages. + handler_type: preset + preset_name: participate + event_type: thread.session.join + event_config: + thread.session.join: + filters: {} + status: active + +metadata: + category: demos + version: "1.0" + demonstrates: + - agent-scoped environment variables + - bearer token authorization headers + - script-backed custom tools diff --git a/agents/internal-caller-agent/env.example b/agents/internal-caller-agent/env.example new file mode 100644 index 0000000..2cd5223 --- /dev/null +++ b/agents/internal-caller-agent/env.example @@ -0,0 +1,5 @@ +# Required after deploying the agent. +# +# This is intentionally agent-scoped instead of org-scoped so only this +# agent's script-backed tools can read it. +AGENT_BEARER_TOKEN=demo_replace_with_random_token diff --git a/agents/internal-caller-agent/sample.yaml b/agents/internal-caller-agent/sample.yaml new file mode 100644 index 0000000..9de794b --- /dev/null +++ b/agents/internal-caller-agent/sample.yaml @@ -0,0 +1,11 @@ +schema_version: 2 +version: v0.1.0 +name: "Internal Caller Agent" +tagline: "Shows how an agent can call an authenticated API with an agent-scoped bearer token." +min_cli_version: "0.28.0" + +steps: + - type: upload_scripts + source_dir: scripts + - type: deploy_agent + template_file: agent.yaml diff --git a/agents/internal-caller-agent/scripts/get-auth-status.aascript b/agents/internal-caller-agent/scripts/get-auth-status.aascript new file mode 100644 index 0000000..59db699 --- /dev/null +++ b/agents/internal-caller-agent/scripts/get-auth-status.aascript @@ -0,0 +1,21 @@ +// Demonstrates an authenticated outbound API call with an agent-scoped +// bearer token. The full token is not returned to the agent. + +let http = import("requests") + +let response = unwrap(http.get("https://httpbin.org/bearer", { + headers: {"Authorization": "Bearer " + env.AGENT_BEARER_TOKEN}, + timeout: 30 +})) + +let echoed_token = response.body.token || "" + +{ + ok: response.status == 200, + status: response.status, + endpoint: "https://httpbin.org/bearer", + authenticated: response.body.authenticated, + token_was_echoed: echoed_token != "", + token_matched_secret: echoed_token == env.AGENT_BEARER_TOKEN, + token_source: "agent_scoped_env_var:AGENT_BEARER_TOKEN" +} diff --git a/samples.json b/samples.json index 0b54bba..f7bb03f 100644 --- a/samples.json +++ b/samples.json @@ -97,6 +97,14 @@ "min_cli_version": "0.28.0", "kind": "solution" }, + { + "slug": "internal-caller-agent", + "name": "Internal Caller Agent", + "tagline": "Shows how an agent can call an authenticated API with an agent-scoped bearer token.", + "current_version": "v0.1.0", + "min_cli_version": "0.28.0", + "kind": "agent" + }, { "slug": "onboarding-qa", "name": "Onboarding Q&A",