Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions docs/guides/composing-skills.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -226,21 +226,33 @@ Two habits keep it safe:
only when the agent actually reads them.
- **Subtract the cost where you budget context.** If your agent sizes a
tool-result envelope against the window, take the loaded set's cost out of that
budget. The email agent's bulk-triage path does exactly this.
budget. The email agent's bulk-triage path does exactly this — and measure the
result: its three-skill `personal` set costs ~1,334 prompt tokens, cutting that
envelope from 6,144 to 4,810 (the four-skill `work` set, to 4,070). Losing a
fifth to a third of the room for tool results is why its sets are currently
switched off pending an eval.

## A shipped example
## A worked example in the tree

The [email agent](https://github.com/amd/gaia/tree/main/hub/agents/email) is the
reference consumer. It bundles six instruction-only skills and declares two sets —
`personal` (triage, newsletter digests, travel itineraries) and `work` (triage,
meeting scheduling, action-item extraction, escalation routing), with
`inbox-triage` in both.

Its selector keys off the connected mailbox: a personal Microsoft account
activates `personal`, a work/school account activates `work`. A Gmail mailbox
carries no equivalent signal, so its kind is unknown, the selector returns `None`,
and the manifest's default applies — explicitly, never by assuming a work mailbox
is personal.
reference implementation of this pattern. It bundles six instruction-only skills
and its manifest carries two sets — `personal` (triage, newsletter digests,
travel itineraries) and `work` (triage, meeting scheduling, action-item
extraction, escalation routing), with `inbox-triage` in both — plus a selector
that keys off the connected mailbox: a personal Microsoft account picks
`personal`, a work/school account picks `work`. A Gmail mailbox carries no
equivalent signal, so its kind is unknown, the selector returns `None`, and the
manifest's default applies — explicitly, never by assuming a work mailbox is
personal.

<Note>
Read it as a code reference, not as "skills are on by default in a shipped
agent": the email agent's `skill_sets:` and `default_skill_set:` blocks are
**commented out** in `gaia-agent.yaml` pending an eval that shows the skills
help, so on the shipped binary it loads no skills at all. The wiring is what's
worth copying; whether to turn it on is an eval question for your own agent
too.
</Note>

## Related

Expand Down
73 changes: 32 additions & 41 deletions docs/guides/email.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -443,49 +443,40 @@ When the calendar tools run (`list_calendar_events`, `accept_invite`, `decline_i
(Google calendar scope was skipped during consent). Without an explicit provider setting the
agent used to pick Google and fail. It now picks Outlook correctly.

## Skill sets: personal vs work mailbox
## Skill sets: disabled

The agent bundles six **Agent Skills** — short playbooks it loads into its own
prompt — and activates one **set** of them per launch, so the same agent behaves
appropriately for the mailbox in front of it:
<Warning>
**Agent Skills are switched off for the email agent.** The `skill_sets:` and
`default_skill_set:` blocks in `hub/agents/email/python/gaia-agent.yaml` are
commented out, so the agent loads **zero** skills: `active_skill_set` is `None`
and `loaded_skills` is empty. They were turned on by default with no eval run
behind them, and an active `personal` set cost ~1,334 prompt tokens — cutting
the bulk-triage result envelope from 6144 to 4810 (`work`, to 4070). The agent
is back to its full 6144-token envelope.
</Warning>

| Set | Skills |
|---|---|
| `personal` (default) | `inbox-triage`, `newsletter-digest`, `travel-itinerary` |
| `work` | `inbox-triage`, `meeting-scheduling`, `action-item-extraction`, `escalation-routing` |

`inbox-triage` is in both — sets overlap rather than partition.

The set is chosen in this order:

1. **Explicit** — `gaia-agent-email serve --skill-set work`, or
`GAIA_EMAIL_SKILL_SET=work`. An undeclared name fails at startup naming the
valid sets; it never falls back.
2. **Mailbox account type** — a personal Microsoft account activates `personal`,
a work/school account activates `work`. The kind is derived from the id_token
`tid` claim when the mailbox is connected and stored with the connection, so
nothing is re-derived per run. Pin it with `GAIA_EMAIL_ACCOUNT_TYPE=work`.

<Note>
Microsoft is now two connectors — `microsoft` (personal) and `microsoft_work`
— and the email agent only declares `microsoft` so far
([#2629](https://github.com/amd/gaia/issues/2629) tracks wiring the work one).
Until that lands, the automatic `work` path is not reachable through this
agent: pin the set with `GAIA_EMAIL_ACCOUNT_TYPE=work` or `--skill-set work`.
</Note>
3. **`default_skill_set`** (`personal`) — applies when the kind is unknown.

**Gmail resolves to the default.** Google has no equivalent of the Microsoft
tenant claim, so a Gmail mailbox's kind is genuinely unknown and the declared
default applies. In practice that means a *work* Gmail mailbox gets the
`personal` set — by declared default, and logged, rather than by anything
inferring the kind from your mail. **If you drive a work Gmail account, set
`GAIA_EMAIL_ACCOUNT_TYPE=work` or pass `--skill-set work`.** The same holds for a
Microsoft mailbox connected before this shipped: it carries no recorded kind
until you reconnect it.

Inspect what loaded with `gaia skill list`, or read the startup log line naming
the active set and the rule that chose it. Full mechanism:
The agent still *bundles* six instruction-only skills at
`gaia_agent_email/skills/<name>/SKILL.md` — `inbox-triage`, `newsletter-digest`,
`travel-itinerary`, `meeting-scheduling`, `action-item-extraction`,
`escalation-routing` — and the selection machinery
(`EmailTriageAgent.select_skill_set()`, the account-type mapping,
`GAIA_EMAIL_SKILL_SET`, `--skill-set`) is all still there. It is simply inert
while no sets are declared.

Pinning a set is therefore a startup error, not a silent no-op:

```console
$ gaia-agent-email serve --skill-set personal
gaia-agent-email: error: --skill-set requested skill set 'personal', but this
agent declares no skill sets — Agent Skills are switched off in this build. Drop
the option, or uncomment the 'skill_sets:' and 'default_skill_set:' blocks in
gaia-agent.yaml.
```

To re-enable, uncomment **both** blocks together — a non-empty `skill_sets:`
with no `default_skill_set:` fails validation — and run the eval before
shipping it on. `gaia skill list` shows what is discoverable; the startup log
names what actually loaded. Full mechanism:
[Composing skills and skill sets](/guides/composing-skills).

## Dev mode: run the email agent from source
Expand Down
26 changes: 18 additions & 8 deletions docs/spec/agent-skills.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ discovery roots with auditable precedence, progressive disclosure,
`gaia skill list|info|create|import|export`. Plus the declarative
`skills:` / `skill_sets:` manifest blocks and per-launch
[skill-set selection](#skill-sets) ([#2466](https://github.com/amd/gaia/issues/2466)),
whose reference consumer is the email agent.
whose reference implementation is the email agent (which currently ships with its
sets commented out — see [below](#reference-implementation-the-email-agent)).

**Still proposed:** the permission sandbox and tier-ceiling enforcement
([#1019](https://github.com/amd/gaia/issues/1019)), the skill marketplace and
Expand Down Expand Up @@ -413,17 +414,26 @@ adopted the blocks, whose behaviour is unchanged.
into the system prompt, which is prompt budget the agent's own tool results no
longer have. An agent with a tight context envelope must account for the loaded
set's cost, not assume it is free — the email agent's bulk-triage path subtracts
it from its result-envelope budget for exactly this reason. Budget the set, and
keep bodies short.
it from its result-envelope budget for exactly this reason — and its three-skill
`personal` set still cut that envelope from 6,144 tokens to 4,810 (the four-skill
`work` set, to 4,070). Budget the set, and keep bodies short.
</Note>

### Reference consumer: the email agent
### Reference implementation: the email agent

The email agent ([`hub/agents/email/`](https://github.com/amd/gaia/tree/main/hub/agents/email))
is the shipped worked example. It bundles six instruction-only skills and declares
two sets — `personal` (triage, newsletter digests, travel itineraries) and `work`
(triage, meeting scheduling, action-item extraction, escalation routing) — with
`inbox-triage` deliberately in both.
is the worked example in the tree. It bundles six instruction-only skills and its
manifest carries two sets — `personal` (triage, newsletter digests, travel
itineraries) and `work` (triage, meeting scheduling, action-item extraction,
escalation routing) — with `inbox-triage` deliberately in both.

<Warning>
**It ships with those blocks commented out**, pending an eval that shows the
skills improve triage, so the released agent resolves no set and loads no skills
— `active_skill_set is None`, `loaded_skills == {}`, and a `--skill-set` request
is a startup error. Treat it as a wiring reference, not as evidence that a
shipped agent has a set active.
</Warning>

Its selector maps the connected mailbox's account type onto a set: a personal
Microsoft account gets `personal`, a work/school account gets `work`. The kind is
Expand Down
29 changes: 16 additions & 13 deletions hub/agents/email/npm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ behind any entry — API shapes, endpoints, and version semantics — see
follow-up referring to something an earlier turn surfaced has something
to resolve against. Leave it unset and nothing changes (#2829, schema
2.12).
- **The agent's built-in skills ship switched off, so the whole context window
goes back to your mail.** The six skills below are still in the package, but
no set is active and none of them loads: nothing yet shows they make triage
better, and an active set was consuming most of the room the agent had for
bulk-triage results. A personal and a work mailbox get identical behaviour
again, and `--skill-set` / `GAIA_EMAIL_SKILL_SET` now fail at startup saying
there are no sets to pick rather than quietly doing nothing. Nothing else
changes — same endpoints, same tools, same permissions.
- **One inbox triage card instead of two that disagreed.** Asking the agent
to triage your inbox used to draw two summary boxes from two separate scans
at different depths — one might say "nothing needs you" while the other,
Expand Down Expand Up @@ -72,19 +80,14 @@ behind any entry — API shapes, endpoints, and version semantics — see
still uses your exact wording when you hand it over yourself. Sending is
unchanged — every draft still needs your confirmation before it goes out
(#2524).
- **The agent now works differently for a personal mailbox than for a work one.**
It used to bring exactly the same instincts to both: the same triage advice for
a mailbox full of newsletters and flight confirmations as for one full of
meeting invites and things people are waiting on you for. It now ships six
built-in skills and turns on one set of them per run — `personal` (inbox triage,
newsletter digests, trip itineraries) or `work` (inbox triage, meeting
scheduling, action items, escalation). For an Outlook mailbox it picks the set
itself from the kind of Microsoft account you connected. Gmail doesn't say which
kind it is, so a Gmail mailbox gets `personal` unless you pin one — start the
sidecar with `extraArgs: ["--skill-set", "work"]` or
`env: { GAIA_EMAIL_SKILL_SET: "work" }`. This changes how the agent approaches
your mail, not what it can do: same endpoints, same tools, same permissions, no
schema bump (#2466).
- **Six built-in skills, and the groundwork for treating a personal mailbox
differently from a work one — shipped switched off.** The skills (`personal`:
inbox triage, newsletter digests, trip itineraries; `work`: inbox triage,
meeting scheduling, action items, escalation) and the machinery that picks a
set from the kind of Microsoft account you connected are in the package, but
no set is declared, so none of it is active — see the first entry above.
Turning it on is a change inside the agent; nothing in your integration
changes either way (#2466).
- **Opt-in preview: small on-device models can now decide phishing flags and
triage categories instead of keyword rules.** Turn it on with
`GAIA_EMAIL_USE_SLM=true` on the sidecar (or `use_slm=True` in config).
Expand Down
43 changes: 15 additions & 28 deletions hub/agents/email/npm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,34 +135,21 @@ for a local page to test triage, drafting, and a live send.

## Personal mailbox vs work mailbox

The agent behaves differently depending on the kind of mailbox it's connected to.
It ships six built-in **skills** — short playbooks it loads into its own thinking —
and turns on one **set** of them per run:

- **Personal** (the default): inbox triage, newsletter digests, and building a trip
itinerary out of scattered booking confirmations.
- **Work**: inbox triage, meeting scheduling, pulling action items out of threads,
and deciding what needs escalating.

It picks the set automatically for an Outlook mailbox — a personal Microsoft
account gets the personal set, a work or school account gets the work set. Two
mailboxes need you to say which you want: Gmail, which doesn't expose the kind at
all, and a work or school Microsoft account, whose connector the agent doesn't
offer yet ([#2629](https://github.com/amd/gaia/issues/2629)). Both fall back to
the personal set until you pin one.

To pin it yourself, start the sidecar with the set you want:

```ts
const sidecar = await startSidecar({
binaryPath,
port: 8131,
extraArgs: ["--skill-set", "work"], // or env: { GAIA_EMAIL_SKILL_SET: "work" }
});
```

This changes how the agent *approaches* your mail, not what it's allowed to do —
the tools, permissions, and API are identical either way. Full detail in
The package ships six built-in **skills** — short playbooks the agent can load into
its own thinking — grouped into a `personal` set (inbox triage, newsletter digests,
trip itineraries) and a `work` set (inbox triage, meeting scheduling, action items,
escalation).

**They are switched off in this release.** Nothing is loaded at launch and a
personal and a work mailbox get identical behaviour, because there is no eval
evidence yet that the skills improve triage. The skill files stay in the package,
inert, and the agent's full context window goes to your mail instead of to skill
text.

Nothing for you to do or change: there is no set to pin, and passing
`--skill-set` / `GAIA_EMAIL_SKILL_SET` fails at startup saying so rather than
quietly doing nothing. Re-enabling is a change inside the agent, not in your
integration. Full detail in
[`SPEC.md`](https://github.com/amd/gaia/blob/agent-pkg-email-v0.5.0/hub/agents/email/npm/SPEC.md).

## How it works
Expand Down
68 changes: 24 additions & 44 deletions hub/agents/email/npm/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ Follow these steps to wire it into an app.
> **This file is NOT one of the agent's own skills.** It is the integration
> playbook — how *you* wire this npm package into an app. The sidecar separately
> bundles six **Agent Skills** at `gaia_agent_email/skills/<name>/SKILL.md`, which
> are instructions the *email agent itself* loads into its own prompt at runtime.
> Same filename, different artifact: don't load those into your assistant, and
> don't ship this one as an agent skill. See
> [Skill sets](#skill-sets--pin-the-agents-behaviour-for-a-work-mailbox) below.
> are instructions the *email agent itself* would load into its own prompt at
> runtime — currently **disabled**, so none of them loads. Same filename,
> different artifact: don't load those into your assistant, and don't ship this
> one as an agent skill. See
> [Skill sets](#skill-sets--disabled-in-this-release) below.

## 1. Install

Expand Down Expand Up @@ -342,44 +343,25 @@ rather than returning the same 200 shape a real, found-nothing cycle would (#252
The Python host also ships a thin-client CLI over this same surface:
`gaia email autonomy {status|set-level|pause|resume|run|trust|kill}` (#2516).

## Skill sets — pin the agent's behaviour for a work mailbox
## Skill sets — disabled in this release

The agent bundles six Agent Skills and activates one **set** per launch, so it
approaches a personal mailbox differently from a work one. Nothing in the API
changes — same endpoints, same tools, same permissions — only how the agent
reasons about the mail.
The sidecar bundles six Agent Skills (`personal`: `inbox-triage`,
`newsletter-digest`, `travel-itinerary`; `work`: `inbox-triage`,
`meeting-scheduling`, `action-item-extraction`, `escalation-routing`), but the
agent's manifest currently declares **no sets**, so **none of them loads**. A
personal and a work mailbox get identical behaviour. This is deliberate: the
skills are held back until an eval run shows they improve triage.

| Set | Skills it loads |
|-----|-----------------|
| `personal` (default) | `inbox-triage`, `newsletter-digest`, `travel-itinerary` |
| `work` | `inbox-triage`, `meeting-scheduling`, `action-item-extraction`, `escalation-routing` |
What that means for your integration:

**It usually chooses for itself.** For an Outlook mailbox, GAIA records at connect
time whether the Microsoft account is personal or work/school (from the `tid`
tenant claim in the OAuth `id_token`), and the agent maps that onto the matching
set. **Gmail carries no equivalent claim**, so a Gmail-only mailbox has an unknown
kind and resolves through the manifest default — `personal`. A work Gmail mailbox
is therefore something you should pin explicitly.

Two ways to pin it at spawn time, both via `startSidecar` — use either, not both:

```ts
const sidecar = await startSidecar({
binaryPath,
port: 8131,
// CLI flag: validated against the declared sets, so a typo fails at startup.
extraArgs: ["--skill-set", "work"],
// Env var, equivalent — and the form to use if you spawn the binary yourself:
// env: { GAIA_EMAIL_SKILL_SET: "work" },
});
```

Either one pins the set for **every** session that sidecar serves and overrides the
mailbox-derived choice. To let the agent do the mapping but tell it what kind of
mailbox it has, set `GAIA_EMAIL_ACCOUNT_TYPE` to `personal` or `work` instead.

Only `personal` and `work` are declared. An undeclared name never silently falls
back — the sidecar refuses to start and names the valid sets.
- **Do not pass `--skill-set` or `GAIA_EMAIL_SKILL_SET`.** Any value fails at
startup with `... but this agent declares no skill sets — Agent Skills are
switched off in this build.` There is no working name. This is fail-loud
behaviour, not a bug to work around.
- `GAIA_EMAIL_ACCOUNT_TYPE` still validates but selects nothing.
- Nothing in the API changes either way — same endpoints, same tools, same
permissions. Re-enabling happens inside the agent's `gaia-agent.yaml`; your
code does not change.

## Running in a server / long-lived app

Expand Down Expand Up @@ -480,11 +462,9 @@ Until then the binary boots, but the first `triage` returns **HTTP 502**.
routes for them yet, so don't look for `client.scheduleSend()` /
`client.snooze()` / a voice, follow-up, or waiting-on-you method — they
don't exist (and none of these moves `SCHEMA_VERSION`).
- **A Gmail mailbox always gets the `personal` skill set unless you pin one.** The
set is normally derived from the Microsoft account kind, and Gmail exposes no
equivalent signal — so pass `--skill-set work` (or `GAIA_EMAIL_SKILL_SET=work`)
for a work Gmail mailbox, or the agent won't load the meeting / action-item /
escalation skills. This affects judgement only; no endpoint or tool changes.
- **`--skill-set` / `GAIA_EMAIL_SKILL_SET` always fail right now.** Agent Skills
are disabled in this release, so the agent declares no sets and every name is
invalid. Don't wire either into your spawn options.
- **ESM-only.** `require("@amd-gaia/agent-email")` fails; use `import` / dynamic
`import()`.

Expand Down
Loading
Loading