Satscryption

hermes-a365

Use when integrating a Hermes agent into the Microsoft 365 ecosystem — agent-as-tenant-directory-identity (AI Teammate / agentic user) or Copilot Chat surfacing (Custom Engine Agent + Azure Bot Service). Distinct from Hermes' sibling Teams adapter (`plugins/platforms/teams/`) which covers classic Bot-Framework Teams chat. Wraps the GA `Microsoft.Agents.A365.DevTools.Cli` verbs, ships the BF activity bridge that backs the `agent365` gateway platform, and emits both AI Teammate and Custom Engine Agent manifests.

Satscryption 2 Updated 1mo ago

Resources

10
GitHub

Install

npx skillscat add satscryption/hermes-a365

Install via the SkillsCat registry.

SKILL.md

Hermes A365

Overview

Hermes-A365 is the M365 Copilot ecosystem path for Hermes agents.
It covers the surfaces a classic Bot Framework Teams bot structurally
cannot reach:

  • Path A — AI Teammate (M365 agentic user): the Hermes agent
    appears as a first-class agentic identity in your M365 tenant
    directory, in the "Built for your org" picker, in M365 People
    search, and in agentic-user audit trails. Teams 1:1 chat with
    M365-native identity. No Azure subscription required.
  • Path B — Custom Engine Agent (Azure Bot Service + 1.21 manifest):
    the Hermes agent appears in M365 Copilot Chat's agents picker, in
    Copilot side-panels inside Word / Excel / PowerPoint / Outlook,
    and reaches the Copilot fabric's invoke surfaces (Microsoft Search,
    Outlook compose-action). Requires an Azure subscription so the
    blueprint Entra app can be registered as an Azure Bot Service
    resource.

Both paths share the same blueprint Entra app + service principal +
bot endpoint, and operators with both prerequisites can run both
surfaces from one Hermes-A365 install.

Distinct from Hermes' sibling Teams adapter at
plugins/platforms/teams/adapter.py (shipped v2026.4.30 + in-flight
work in hermes-agent#10037 and #13767). That adapter is the right
tool for generic Teams chat bots — DM, channels, group chats,
threading, file attachments — using classic Bot Framework with an
Azure App Registration + client secret / certificate / Managed
Identity. It gives no M365 directory identity and no Copilot Chat
surfacing.

Microsoft Agent 365 (A365, GA 2026-05-01) is the governance / identity
/ observability control plane Hermes-A365 plugs into: Entra-backed
agentic identity, tenant licensing, agent blueprints, MCP-mediated
Microsoft 365 data access ("Work IQ"), OpenTelemetry, and the
channel adapters for Teams / Outlook / M365 Copilot.

The wrapper is built directly on the GA Microsoft.Agents.A365.DevTools.Cli
verbs documented in `references/a365-cli-reference.md`.
The skill composes them into idempotent plan/apply flows, fills the
gaps the CLI doesn't cover (license decision, admin consent grant,
runtime .env generation, Custom Engine Agent manifest transform),
and ships a Bot Framework activity bridge + Hermes gateway platform
plugin (agent365) that round-trips messages between A365 and the
Hermes agent loop.

When to Use

Use when the user wants any of:

  • Hermes registered as a first-class M365 agentic identity
    appears in the tenant directory, agentic-user audit, "Built for
    your org" picker. Path A.
  • Hermes available in M365 Copilot Chat (agents picker /
    @-mention / side-panels in Word / Excel / PowerPoint / Outlook).
    Path B. Requires Azure subscription.
  • A Bot Framework activity bridge backed by A365 governance
    (Entra-backed identity, MCP-mediated Microsoft 365 data, OTLP
    audit trails).
  • Migrating an OpenClaw-on-A365 deployment to Hermes (the
    blueprint stays; only the runtime endpoint changes).

Don't use when:

  • The goal is a generic Teams chat bot with no M365 directory
    identity or Copilot surfacing — use Hermes' sibling Teams adapter
    (plugins/platforms/teams/, shipped v2026.4.30). It handles
    DM / channels / group / threading / file attachments via classic
    Bot Framework without A365 prerequisites.
  • Generic Microsoft Graph access is the goal — use a Graph-only skill.
  • Deploying a Bot Framework bot outside any M365 / A365
    governance surface — pick a classic BF skill.
  • Setting up OpenAI Agents SDK or another framework end-to-end — A365
    governs the runtime; pick the appropriate framework skill for the
    agent itself.

Prerequisites

  • A Microsoft 365 tenant where the operator has Global Administrator
    or Agent Administrator role and is enrolled in Microsoft's
    Frontier Preview Program.
  • The A365 CLI on PATH: Microsoft.Agents.A365.DevTools.Cli (.NET tool,
    ships as a365). Only the .NET tool ships at
    GA — the npm atk variant referenced in pre-GA documentation never
    landed. No CLI build is currently live-verified clean for
    Microsoft#408: the secret-persistence regression still reproduced on
    1.1.181 during the 2026-05-15 R9 walk. Doctor therefore warns for all
    versions and live setup should keep --auto-recover-secret enabled
    until a fixed build is walked clean.
  • az CLI ≥ 2.55.0, signed into the target tenant. Many a365
    subcommands shell out to az for Entra reads.
  • PowerShell 7+ (pwsh) on PATH. The CLI invokes pwsh for some
    setup steps; missing pwsh causes a365 setup requirements to fail.
  • A custom Entra client app (Microsoft's convention: display name
    Agent 365 CLI) registered in the tenant. The CLI uses it as the
    device-code/auth-code client. Doctor verifies discoverability via the
    signed-in az context.
  • An OS keychain: macOS Security or Linux libsecret (secret-tool).
    Windows is not yet supported.
  • A tenant license: either the Agent 365 add-on ($15/user/month) or
    Microsoft 365 E7 ($99/user/month). The skill never purchases — it
    recommends; see `references/license-cost-table.md`.
  • A Hermes harness with hermes-a365 installed into its venv
    (~/.hermes/hermes-agent/venv/bin/pip install 'hermes-a365[bridge]')
    so the plugin loader auto-discovers agent365 via the
    hermes_agent.plugins entry point, plus plugins.enabled and a
    gateway.platforms.agent365 block configured in
    ~/.hermes/config.yaml. The README quickstart walks through this
    end-to-end.

Core procedures

State-mutating subcommands default to dry-run; pass --apply to
execute. Repeated invocation converges to the same state.

hermes a365 doctor

Read-only environment probe. Exit 0/1/2. Probes: a365, az (signed
in), pwsh, the Agent 365 CLI Entra app, network reachability
(login.microsoftonline.com / graph.microsoft.com), keychain,
~/.hermes/.env, Hermes harness. Frontier Preview enrollment is not
auto-verifiable.

hermes a365 license --users <n> --agents <n> --plan <E3|E5|...>

Read-only. Recommends the A365 add-on or E7 based on the decision matrix
in `references/license-cost-table.md`.
Records the chosen model in ~/.hermes/.env as A365_LICENSE_MODEL.

hermes a365 register --agent-name <name> [--tenant-id <id>] [--m365] [--no-endpoint] [--skip-requirements]

Composite plan that orchestrates the three real CLI steps a blueprint
needs:

  1. a365 setup blueprint --agent-name <name> — registers the Entra app
    and service principal that back the blueprint.
  2. a365 setup permissions mcp --agent-name <name> — configures MCP
    OAuth grants and inheritable permissions.
  3. a365 setup permissions bot --agent-name <name> — configures the
    Messaging Bot API OAuth grants.

The CLI itself owns idempotency, JSON shape, and Entra round-trips. The
skill's job is to compose the right argv per step, run them in order via
the Mutator protocol, persist derived display names to
a365.config.json (so subsequent commands refer to consistent
identities), and surface known auth errors:

  • AADSTS500011 (license not propagated): retried up to --retries
    times with --backoff seconds (defaults: 3 × 30 s, mockable in tests
    via sleep_fn).
  • AADSTS90094 (admin consent required): surfaced as
    "deferred — run hermes a365 consent" rather than failing the run.
    The blueprint apps remain created.

--m365 registers the messaging endpoint via MCP Platform.
--aiteammate is intentionally unsupported on register: the wrapper
only runs blueprint + permission setup. Use
hermes a365 publish --aiteammate --apply, then upload and activate the
zip in M365 Admin Centre, to create/bind the AI Teammate agentic user.
--no-endpoint and --skip-requirements are passthroughs to
a365 setup blueprint.

hermes a365 consent

Renders the admin-consent URL from templates/consent-url.txt.j2, opens
it in the default browser (unless --no-open), then polls
a365 query-entra blueprint-scopes every 5 s up to a 5 min timeout.
Idempotent; re-running after grant is a no-op.

hermes a365 instance create <slug> --owner <email> --owner-aad-id <oid> [...]

Pure local config-file writer. The server-side agent identity is created
by a365 setup blueprint (driven by register); this command only
produces the per-agent ~/.hermes/agents/<slug>/.env that runtime
consumers read for slug, owner, OTLP endpoint, and business-hours
metadata.

Inherits A365_APP_ID, A365_TENANT_ID, HERMES_OTLP_ENDPOINT from
~/.hermes/.env. An existing AA_INSTANCE_ID is preserved across
re-runs; business-hours fields from a prior run are also preserved
unless explicitly overridden. The per-agent .env never contains the
blueprint client secret — see pitfall #7 below for where the secret
actually lives.

hermes a365 publish --agent-name <name> [--aiteammate] [--copilot-chat] [--bot-id <guid>] [--use-blueprint] [--tenant-id <id>]

Wraps a365 publish to package the agent manifest into a zip the
operator uploads to the relevant admin surface. The output mode
follows the M365-ecosystem path:

  • --aiteammate (Path A) — emits an AI Teammate manifest
    (agenticUserTemplates, manifestVersion: devPreview). Upload at
    M365 Admin Centre → Agents → Upload custom agent, then activate
    per-user under Agent 365 admin centre. Surfaces in Teams 1:1
    "Built for your org".
  • --copilot-chat (Path B, slice 19u-a in v0.4.0) — emits a
    Custom Engine Agent manifest (manifestVersion: "1.21",
    bots + copilotAgents.customEngineAgents blocks). Upload at
    Teams Admin Center → Manage apps → Upload + assign per-user
    policy
    . Surfaces in M365 Copilot Chat's agents picker + Copilot
    side-panels. Implementation post-processes the GA CLI's AI
    Teammate zip in-place (or to a sibling .copilot-chat.zip when
    combined with --aiteammate).
  • --aiteammate --copilot-chat — emits both side by side
    (Copilot Chat zip lands at <original>.copilot-chat.zip).
  • --bot-id <guid> — overrides the botId written into the
    Custom Engine Agent manifest. Default extraction order:
    webApplicationInfo.idbots[0].botId → manifest top-level
    id (the GA CLI 1.1.174+ AI Teammate emit only populates the
    last).
  • No surface flag — default a365 publish behaviour (registers
    the agent instance via Graph; no zip).

The wrapper surfaces the resulting package path(s) plus the right
admin-surface URL hint for each path. The name.short 30-char
auto-truncate (slice 19r-c) applies to both flavours. Channel
deployment is operator-side in all cases.

Path B additionally requires Azure Bot Service registration
of the blueprint Entra app with the Microsoft Teams channel
enabled — otherwise Microsoft's routing layer won't forward
Copilot Chat activities to /api/messages regardless of manifest
shape. See references/m365-surface-coverage.md for the
prerequisite detail.

hermes a365 status [<slug>]

Per-component report against the verified query-entra surface. Four
components only:

  • local_config — parent ~/.hermes/.env (and per-agent .env if a
    slug is given) parseable + required keys present.
  • blueprint_scopesa365 query-entra blueprint-scopes for the
    agent's blueprint.
  • instance_scopesa365 query-entra instance-scopes for the
    agent's instance.
  • activity_bridge — local PID-file probe (only when a slug is given
    AND bridge.pid exists). When the bridge is running this row reports
    ok; absent pidfile is missing (bridge not currently running) and
    a stale or unreadable pidfile is error.

Exit codes: 0 ok, 1 partial, 2 broken, 3 skill not yet bootstrapped.

hermes a365 cleanup --agent-name <name> [--kinds=...] --confirm=<name> --apply

Destructive teardown. Drives a365 cleanup azureinstance
blueprint (safe → unsafe — App Service first so the runtime stops
before the Entra identity is revoked). Local artefacts under
~/.hermes/agents/<slug>/ are removed after all cloud steps succeed.
--kinds=<subset> runs only the requested kinds. --confirm must
equal --agent-name. The plan is always printed for operator
audit before any mutation.

hermes a365 activity-bridge

The Bot Framework adapter daemon. Two main modes are available either
as a standalone process (operator launches it) or as the runtime that
backs the agent365 Hermes gateway platform plugin (the gateway
loads it in-process when an agent has the agent365 platform enabled).

  • verify --slug <slug> — one-shot diagnostic (config + auth +
    reachability). Exit 0/1/2.
  • serve --slug <slug> --port 3978 — BF webhook adapter daemon.
    Validates inbound activities as AAD-v2 (A365 / MCP Platform
    issues AAD-v2 tokens directly to bot endpoints, not classic BF
    tokens), dedupes BF retries, gates serviceUrl against the
    Microsoft host suffix, and replies through the agentic three-stage
    user-FIC token chain
    (BF S2S → agent FMI → user FIC). Outbound
    goes via replyToActivity against the cached inbound serviceUrl.
  • update-endpoint --agent-name <n> --url <https> — wraps
    a365 setup blueprint --m365 --update-endpoint <url> so operators
    can pin the agent's messaging endpoint to a tunnel URL. Auto-recovers
    from duplicate-name error #140.

Topology: Teams (or other M365 surface) → A365 BF → <tunnel>/api/messages
→ bridge → Hermes agent loop → reply via serviceUrl. Activity-shape
catalogue: `references/activity-protocol-shapes.md`;
operator-side network exposure options:
`references/exposing-the-bot-endpoint.md`.

Surfaces that work today:

  • Path A (AI Teammate) Teams 1:1 chat — validated end-to-end
    across rounds 3 → 8, with BF streaming protocol round-trip
    on round-8 (2026-05-11, v0.3.0). Agent appears in "Built for
    your org" picker.
  • Path A cron-driven proactive sends — shipped in v0.5.0 +
    v0.5.1 (slices 19x-a..e, closes #4 and #27). Wire-validated
    against the live tenant 2026-05-13. Agent365Adapter.send()
    routes through sendToConversation (no replyToId) when the
    current gateway lifetime hasn't captured an inbound for
    chat_id; mints the agentic three-stage user-FIC chain against
    a target-spec built from the persisted ConversationRegistry.
    prune_old_entries + pin / unpin / mark_used mutators
    let operators manage the registry without restarting.
  • Path B (Custom Engine Agent) Copilot Chat — live since
    v0.6.0 (#16 / #34 / #36 closed). Provisioned with the
    bot-service wrapper family (create / verify /
    update-endpoint / cleanup; slice 20), which registers an Azure
    Bot Service against a separate non-agentic Entra app
    (A365_BF_APP_ID / A365_BF_CLIENT_SECRET) — the blueprint app
    can't mint BF S2S tokens (AADSTS82001), so Path B needs its own
    identity. Inbound uses the BF-shaped JWT validator branch (#34);
    outbound replies go via classic BF S2S. Reply delivery:
    Copilot Chat arrives as a groupChat and does not render BF
    streaming, so non-personal turns are coalesced into one
    non-streaming send_reply (#54 / #55); Teams 1:1 uses BF streaming
    with single-stream-per-turn + a stale-stream liveness guard (#62).
    See `references/activity-protocol-shapes.md`
    Streaming and reply delivery; the §11 runbook in
    `references/live-tenant-test.md`
    is the operator provisioning walk.

Sibling-plugin lane (Teams group chat / channels / threading /
file attachments / compose-extension invokes) is out of scope
for Hermes-A365
— use Hermes' classic Teams adapter for
those. See `references/m365-surface-coverage.md`
for the per-surface matrix and the architectural reasoning behind
the split.

Conflict resolution

Conflict Behaviour
Blueprint app already exists for the same name a365 setup blueprint is itself idempotent; register re-runs are safe.
Permissions step fails with AADSTS90094 Reported as "deferred — run hermes a365 consent"; blueprint stays created.
Instance .env exists with a previous AA_INSTANCE_ID instance create preserves the id (cloud state is unchanged).
Cleanup target has no recorded state The corresponding kind is skipped, not errored.
License missing or insufficient register retries AADSTS500011 with backoff; on exhaustion the operator runs hermes a365 license and re-tries.
pwsh missing on PATH Doctor flags it; a365 setup itself fails fast with a CLI error referencing setup requirements.

Common pitfalls

  1. Channel deployment is operator-side. There is no deploy verb.
    publish produces the zip; the M365 admin uploads and approves it.
  2. Delegated permissions, not application permissions. A365
    explicitly requires delegated permissions. Pasting an application-
    permission consent URL silently breaks at runtime.
  3. One agent name, derived sub-names. --agent-name "Inbox Helper"
    produces Inbox Helper Identity and Inbox Helper Blueprint inside
    the CLI. Don't pass those derived names directly — pass the base.
  4. Blueprint slug ≠ agent name. register operates on the CLI
    --agent-name. The local <slug> used in instance create /
    status / cleanup is the per-agent dir name under
    ~/.hermes/agents/. Keep them aligned (lowercased / hyphenated) by
    convention; the skill never silently re-derives one from the other.
  5. License propagation lag. A365 license assignment can lag 5–30
    min after purchase. register retries AADSTS500011 3× with 30 s
    backoff; if you're outside that window, hermes a365 doctor is the
    first port of call.
  6. AA_INSTANCE_ID reuse across re-runs. instance create
    preserves the existing id. Don't manually edit the per-agent .env to
    "reset" it without first running cleanup — the cloud instance will
    linger.
  7. Blueprint client secret on disk in plaintext (macOS / Linux).
    a365 setup blueprint writes the secret to
    a365.generated.config.json — DPAPI-encrypted on Windows,
    plaintext elsewhere. That file and the cleanup -y-emitted
    *.backup-*.json are gitignored; treat both as keychain-grade.
    CLI versions 1.1.171, 1.1.174, and 1.1.181 reproduce Microsoft#408,
    where the secret is minted but persisted as null on macOS / Linux.
    Keep --auto-recover-secret enabled for live setup regardless of
    CLI version until a later build is live-verified fixed; doctor stays
    warning-only across all version branches to avoid a false green.

Verification checklist

  • hermes a365 doctor exits 0.
  • hermes a365 status <slug> shows: local_config ok,
    blueprint_scopes ok, instance_scopes ok.
  • a365 publish zip uploaded via the M365 Admin Centre and
    approved for the target DLP scope.
  • Test message in Teams (or other approved channel) returns an
    Adaptive Card from the agent.
  • OTLP trace visible in the A365 admin centre for the test message.
  • hermes a365 cleanup --agent-name <name> (dry-run) lists exactly
    the resources the operator expects to remove.

One-shot recipes

Bootstrap a single agent on a clean tenant

hermes a365 doctor                                                     # health check
hermes a365 license --users <n> --agents <n> --plan E5                 # decide license
hermes a365 register --agent-name "<Display Name>"                     # plan
hermes a365 register --agent-name "<Display Name>" --apply --auto-recover-secret
hermes a365 consent                                                    # in-browser grant
hermes a365 instance create <slug> --owner <email> --owner-aad-id <oid> --apply
hermes a365 publish --agent-name "<Display Name>" --aiteammate --apply # produce zip
# Operator: upload the zip in the M365 Admin Centre and activate for users.
hermes a365 activity-bridge verify --slug <slug>                       # bridge preflight
# Run the gateway with the agent365 platform configured (loads the
# bridge in-process via the entry-point-discovered plugin):
hermes gateway run --profile <slug>
hermes a365 status <slug>                                              # final verification

Re-target an existing OpenClaw-on-A365 agent at Hermes

The blueprint stays in place; only the runtime endpoint and per-agent
config change:

hermes a365 instance create <slug> --owner <email> --owner-aad-id <oid> --apply
hermes a365 publish --agent-name "<Existing Display Name>" --apply
# Operator re-uploads the zip; the activity bridge then takes over the
# BF subscription URL the previous runtime was using.

Decommission an agent cleanly

hermes a365 cleanup --agent-name "<Display Name>"                              # plan
hermes a365 cleanup --agent-name "<Display Name>" --apply --confirm="<Display Name>"

--kinds=instance,blueprint skips Azure when the App Service was
provisioned out-of-band. Tenant-wide infrastructure (Frontier Preview
enrollment, the custom Agent 365 CLI client app, license) is never
touched.


Subcommand implementations live under src/hermes_a365/; each is a
thin CLI over a planner + applier pair parameterised by a Mutator
protocol so the apply path is unit-testable without the live A365 CLI.
Packaged Jinja templates resolve via importlib.resources against
hermes_a365._data.templates; dated reference snapshots live under
references/. For per-subcommand flags see hermes a365 <verb> --help;
the original v0.1 design draft is archived at
docs/historical/SPEC-v0.1-draft.md.