Skip to content

Commit e723777

Browse files
authored
Merge pull request #5300 from Agenta-AI/release/v0.105.0
[release] v0.105.0
2 parents 765da6e + 2853e37 commit e723777

2,526 files changed

Lines changed: 324040 additions & 14879 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/agenta-package-practices/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ via `ModalFooter`, theme integration.
134134

135135
## Style utilities and presentational components
136136

137+
When adding or changing UI elements, implement appearance and interaction states for both light and dark themes, and verify both before considering the work complete.
138+
137139
```typescript
138140
import {cn, textColors, bgColors} from "@agenta/ui"
139141

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
---
2+
name: sync-model-catalog
3+
description: Regenerate and refresh the curated agent model catalog (the label/description/pricing/ratings behind the agent model picker). Use when the pinned @earendil-works/pi-ai version bumps, when a Claude Code build changes its accepted alias set, or before a release when the curated Claude/Pi facts (lineup, pricing, ratings) need refreshing from current public sources. Owns the data files under sdks/python/agenta/sdk/agents/data/; never edits capabilities.py logic.
4+
allowed-tools: Read, Edit, Write, Grep, Glob, Bash, WebSearch, WebFetch
5+
user-invocable: true
6+
---
7+
8+
# Sync model catalog
9+
10+
Keeps the agent model catalog honest. The catalog is the curated decoration over each harness's
11+
accepted model set: clean labels, one-sentence descriptions, real pricing, and 1-5 ratings, keyed
12+
by the id the harness accepts. It is published additively next to the ids-only `models` map on the
13+
harness capability record (`capabilities.py`).
14+
15+
Design and rationale:
16+
`docs/design/agent-workflows/projects/model-catalog-schema/{design.md,plan.md}`.
17+
18+
## What it owns
19+
20+
Three JSON data files under `sdks/python/agenta/sdk/agents/data/`, loaded by
21+
`sdks/python/agenta/sdk/agents/model_catalog.py`:
22+
23+
- `pi_models.generated.json` — machine-generated from pi-ai. Objective facts only (name / pricing /
24+
context_window / modalities), `source: "pi_generated"`. **Never hand-edit.**
25+
- `pi_models.curated.json` — human overlay for the generated file (id -> `{label?, description?,
26+
ratings?}`), merged onto the generated facts at load. Survives regeneration.
27+
- `claude_models.curated.json` — hand-curated Claude alias entries (facts + judgments),
28+
`source: "curated"`.
29+
30+
It never edits `capabilities.py` logic — only these data files.
31+
32+
## The three jobs
33+
34+
### 1. Regenerate the Pi file (on a pi-ai version bump)
35+
36+
The generator reads the pinned pi-ai `models.generated` for the providers Agenta reaches (the
37+
vault-mapped providers plus `openai-codex`) and emits one entry per model. pi-ai provider names are
38+
mapped to Agenta's vocabulary (`google`->`gemini`, `together`->`together_ai`); ids are
39+
`<agenta-provider>/<pi-model-id>`.
40+
41+
```bash
42+
# From repo root. Point at the pinned pi-ai in the runner's node_modules (the .pnpm path includes
43+
# the version — resolve it with the glob).
44+
MODELS=$(ls services/runner/node_modules/.pnpm/@earendil-works+pi-ai@*/node_modules/@earendil-works/pi-ai/dist/models.generated.js | head -1)
45+
node .agents/skills/sync-model-catalog/generate_pi_models.mjs "$MODELS" \
46+
sdks/python/agenta/sdk/agents/data/pi_models.generated.json
47+
```
48+
49+
Detect the bump from a lockfile diff on `@earendil-works+pi-ai@<version>`. The `_generator` field
50+
in the output records the exact pi-ai version. The curated overlay is untouched — only the
51+
`.generated.json` is rewritten, so the merge on load re-applies the human judgments.
52+
53+
### 2. Sync Claude to the live accepted set (needs a running runner)
54+
55+
`claude_models.curated.json` must cover exactly the aliases the Claude harness accepts. Probe the
56+
live set: start a Claude session on a running runner and read the model config options (the same
57+
`getConfigOptions` call `allowedModels` uses in `services/runner/src/engines/sandbox_agent/model.ts`).
58+
Reconcile in two directions only: add a curated entry for any accepted alias the file lacks (seed its
59+
facts from pi-ai's `anthropic` block), and remove any entry the harness no longer accepts. This is how
60+
a new alias (e.g. a `fable` alias, if a Claude Code build starts accepting it) enters the catalog.
61+
Requires an authenticated Claude session, so it is a manual/periodic step, not a CI gate.
62+
63+
Note: today the accepted set is `default/sonnet/opus/haiku` plus their `[1m]` variants
64+
(`CLAUDE_MODEL_ALIASES` in `capabilities.py`). There is no `fable` alias yet; Fable reaches the
65+
picker through the Pi `anthropic` block. Do not add a `fable` Claude entry until the live probe
66+
confirms the harness accepts it.
67+
68+
### 3. Refresh curated metadata from current public sources (before a release / on demand)
69+
70+
Labels, descriptions, and ratings state a model's *current* standing, which a language model's
71+
training data gets wrong (the Anthropic frontier is Fable 5, above Opus, as of mid-2026). Look up the
72+
current lineup, pricing, and relative standing from the vendor's pages and announcements (WebSearch +
73+
WebFetch), then propose updated descriptions and ratings for a human to confirm. Never write a rating
74+
from memory. Validate the 1-5 range and flag any entry whose facts you could not verify. Ratings:
75+
higher is better on every axis; `cost` is cost-efficiency (5 = cheapest).
76+
77+
## Validate
78+
79+
The pydantic loader enforces the schema (including the 1-5 rating range) on load, and the unit test
80+
locks coverage and the overlay merge:
81+
82+
```bash
83+
cd sdks/python && uv run --no-sync python -m pytest \
84+
oss/tests/pytest/unit/agents/connections/test_model_catalog.py -q
85+
```
86+
87+
A malformed data file fails loud there (and at import in `capabilities.py`, which then publishes an
88+
empty catalog rather than crashing `/inspect`).
89+
90+
## When to run
91+
92+
- Job 1 on a pi-ai bump (automatable from a lockfile diff).
93+
- Jobs 2 and 3 before a release or on demand (need a live session and a web lookup).
94+
95+
The skill writes files and a proposal; a human reviews the curated changes and commits.
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Generate the Pi model-catalog data file from the pinned `@earendil-works/pi-ai`
2+
// `models.generated` catalog. This is job 1 of the `sync-model-catalog` skill.
3+
//
4+
// It reads the pi-ai model definitions for the providers Agenta reaches (the vault-mapped
5+
// providers plus the `openai-codex` subscription) and emits one `ModelCatalogEntry` per model
6+
// with `source: "pi_generated"` and the objective facts filled from pi-ai (name / pricing /
7+
// context_window / modalities). The curated fields (label / description / ratings) are left
8+
// absent — a human adds those in the sibling `pi_models.curated.json` overlay, which the SDK
9+
// loader merges on top of this file. Regeneration only ever rewrites this generated file, so the
10+
// overlay survives a bump.
11+
//
12+
// Run from the runner package (so `@earendil-works/pi-ai` resolves):
13+
// node .agents/skills/sync-model-catalog/generate_pi_models.mjs \
14+
// services/runner/node_modules/@earendil-works/pi-ai/dist/models.generated.js \
15+
// sdks/python/agenta/sdk/agents/data/pi_models.generated.json
16+
//
17+
// The output JSON carries no inline comments (JSON forbids them); the "generated, do not
18+
// hand-edit" notice lives in the `_generator` envelope field and in the skill README.
19+
20+
import {readFileSync, writeFileSync} from "node:fs";
21+
import {pathToFileURL} from "node:url";
22+
23+
// pi-ai provider name -> Agenta vault provider vocabulary. Agenta's capability table, vault, and
24+
// FE reachability filter speak `gemini`/`together_ai`; pi-ai's catalog keys them `google`/
25+
// `together`. The catalog entry's `provider` (and the `provider/` prefix on its `id`) uses the
26+
// Agenta vocabulary so it lines up with the rest of the system. The exact `provider/id` string the
27+
// live Pi harness accepts is verified by the skill's live-probe job, not asserted here.
28+
const PROVIDER_MAP = {
29+
openai: "openai",
30+
anthropic: "anthropic",
31+
google: "gemini",
32+
mistral: "mistral",
33+
groq: "groq",
34+
minimax: "minimax",
35+
together: "together_ai",
36+
openrouter: "openrouter",
37+
"openai-codex": "openai-codex",
38+
};
39+
40+
// The pi-ai provider blocks Agenta reaches: the vault-mapped providers plus the codex subscription.
41+
const PI_PROVIDERS = Object.keys(PROVIDER_MAP);
42+
43+
function piVersion(modelsPath) {
44+
// dist/models.generated.js -> ../package.json (the pi-ai package root).
45+
try {
46+
const pkgUrl = new URL("../package.json", pathToFileURL(modelsPath));
47+
const pkg = JSON.parse(readFileSync(pkgUrl, "utf8"));
48+
return `@earendil-works/pi-ai@${pkg.version}`;
49+
} catch {
50+
return "@earendil-works/pi-ai@unknown";
51+
}
52+
}
53+
54+
function pricing(cost) {
55+
if (!cost) return null;
56+
const out = {
57+
input_per_mtok: cost.input ?? null,
58+
output_per_mtok: cost.output ?? null,
59+
currency: "USD",
60+
};
61+
if (cost.cacheRead != null) out.cache_read_per_mtok = cost.cacheRead;
62+
if (cost.cacheWrite != null) out.cache_write_per_mtok = cost.cacheWrite;
63+
return out;
64+
}
65+
66+
function entryFor(agentaProvider, model) {
67+
return {
68+
id: `${agentaProvider}/${model.id}`,
69+
provider: agentaProvider,
70+
source: "pi_generated",
71+
name: model.name ?? null,
72+
pricing: pricing(model.cost),
73+
context_window: model.contextWindow ?? null,
74+
modalities: Array.isArray(model.input) ? model.input : null,
75+
label: null,
76+
description: null,
77+
ratings: null,
78+
};
79+
}
80+
81+
async function main() {
82+
const modelsPath = process.argv[2];
83+
const outPath = process.argv[3];
84+
if (!modelsPath || !outPath) {
85+
console.error("usage: generate_pi_models.mjs <models.generated.js> <out.json>");
86+
process.exit(2);
87+
}
88+
89+
const {MODELS} = await import(pathToFileURL(modelsPath).href);
90+
91+
const models = [];
92+
for (const piProvider of PI_PROVIDERS) {
93+
const block = MODELS[piProvider];
94+
if (!block) continue;
95+
const agentaProvider = PROVIDER_MAP[piProvider];
96+
for (const model of Object.values(block)) {
97+
models.push(entryFor(agentaProvider, model));
98+
}
99+
}
100+
101+
const doc = {
102+
schema_version: "1",
103+
_generator: {
104+
source: piVersion(modelsPath),
105+
generated_by: ".agents/skills/sync-model-catalog/generate_pi_models.mjs",
106+
generated_at: new Date().toISOString(),
107+
note: "Generated file. Do not hand-edit. Curated fields live in pi_models.curated.json.",
108+
},
109+
models,
110+
};
111+
112+
writeFileSync(outPath, JSON.stringify(doc, null, 2) + "\n");
113+
console.error(`wrote ${models.length} models to ${outPath} from ${doc._generator.source}`);
114+
}
115+
116+
main().catch((e) => {
117+
console.error(e);
118+
process.exit(1);
119+
});

.agents/skills/write-docs/SKILL.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,144 @@ user-invocable: true
99

1010
A short, living set of rules for writing documentation in this repo. Update as we learn what works.
1111

12+
## 0. Write for a reader in a situation
13+
14+
This rule governs every other rule below. Every failure in a doc review traces back to breaking it.
15+
16+
Your reader is a **stranger with a job to do**. They are not your teammate, they did not read the PR, they do not know the codebase, and they do not care about your reasoning. They arrived with a situation and a question. Serve that, nothing else.
17+
18+
Some readers are agents, some are humans. Write for both: an agent needs unambiguous structure, a human needs to find their case fast. Neither needs your narration.
19+
20+
### The four beats
21+
22+
For anything non-trivial (a troubleshooting entry, a decision, a procedure with branches), give the reader these in order. Do not overdo it on simple content.
23+
24+
1. **Context.** What is the situation? Stated plainly, so I can tell whether I am in it.
25+
2. **Relevance.** Is this me? Do I need to act? Let me self-select out fast.
26+
3. **Instructions.** Exactly what to run or change. Concrete, copy-pasteable.
27+
4. **References.** Links to go deeper, if it is complicated.
28+
29+
A section that says "enable the tunnel profile" without telling me *how* has skipped beat 3. A warning that does not tell me whether it applies to me has skipped beats 1 and 2.
30+
31+
### Never assume things about the reader
32+
33+
Do not tell readers what they think, what they usually get wrong, or what their first question is. You do not know them, and the sentence serves you, not them.
34+
35+
- BAD: "This is the part teams usually get wrong."
36+
- BAD: "This is the first question a self-hosting community asks."
37+
- BAD: "You probably want X."
38+
- GOOD: State the fact. Let them decide if it is their case.
39+
40+
### No opinions, no value judgments
41+
42+
Docs state facts and give instructions. They do not give advice you were not asked for, and they do not editorialize.
43+
44+
- BAD: "Keep CPU and memory generous."
45+
- BAD: "This is the right way to do it."
46+
- GOOD: "The default is 2 CPU / 4 GB. To change it, set X."
47+
48+
If a constraint is real, state it as a constraint ("the idle TTL must be below AUTOSTOP, or Daytona stops a sandbox the runner still holds"). That is a fact, not an opinion.
49+
50+
### Only the reader's problem belongs on the page
51+
52+
Cut anything that is not the reader's concern in *their* situation.
53+
54+
- In a how-to for running agents on Daytona, the **code evaluator's** sandbox is not the reader's problem. It confuses them. If the fact matters, it belongs in reference.
55+
- Do not pre-empt failure modes the reader cannot hit. If you just told them to run the snapshot recipe, do not then warn "if the image is missing Pi…". They followed your instructions. It will not be missing.
56+
57+
Ask of every sentence: **in the situation this page is for, does the reader need this?** If not, delete it or move it to reference.
58+
59+
### Parallel things get parallel structure
60+
61+
If two options exist (Daytona and local; Compose and Helm), give them **mirrored sections with the same sub-structure, in the same order, on every page**. A reader learns the shape once and then navigates by it.
62+
63+
- Nest correctly. "What crosses into the Daytona sandbox" belongs **under** Daytona, not floating at the top level.
64+
- If you write a subsection for one option, write the matching one for the other, or explicitly say it does not apply.
65+
- Lead with the option you want most people to use.
66+
67+
### An instruction without a concrete example is not an instruction
68+
69+
"Add your dependencies" tells the reader nothing. Name a plausible thing and show the line. The
70+
reader adapts an example far faster than they invent one from a description.
71+
72+
- BAD: "Add your dependencies to the recipe."
73+
- GOOD: "Add your dependencies to `dockerfile_commands`. For example, the `gh` CLI and Chromium: `RUN apt-get install -y gh chromium`."
74+
- GOOD: "Mount a repository checkout so agents can work on it: `- /srv/repos/my-service:/agenta/workspaces/my-service:rw`. Use `:ro` when they should only read it."
75+
76+
### Use the precise word, not the dramatic one
77+
78+
Vague adjectives, especially security ones, make the reader guess. Say the condition that is
79+
actually true.
80+
81+
- BAD: "Use Daytona when your deployment is exposed." (Exposed to what? Does that include me?)
82+
- GOOD: "Use Daytona when more than one person can start runs on the deployment."
83+
- BAD: "Local runs are not safe."
84+
- GOOD: "Local runs are not isolated from each other. One user's agent can read another's files."
85+
86+
### A pointer is a sentence, not a section
87+
88+
If the answer is "go read that other page", write one sentence with the link where the reader hits
89+
the question. Do not give it a heading and three paragraphs of setup. A section promises content.
90+
91+
### Slugs exist to preserve URLs
92+
93+
Add a `slug:` to a page's frontmatter only when the file moved and the old public URL must keep
94+
working, or when the natural path is wrong. Never add one just because other pages have one.
95+
96+
### Verify every instruction against the thing it instructs
97+
98+
Prose discipline is not enough. An instruction you did not check is a lie with good grammar, and it
99+
costs the reader an afternoon.
100+
101+
Before you write "set X in your values file", open the chart and confirm the key exists. Before you
102+
write "add a `COPY` step", confirm the build has a local context. Before you name an env var,
103+
confirm the code reads it. Before you claim "everything else is documented above", go count.
104+
105+
Real failures caught in review, all of which read fluently:
106+
107+
- BAD: "For Helm, add the volume to the runner pod in your values file." The chart renders **no**
108+
user volumes and `values.schema.json` has no volumes key. The instruction is impossible.
109+
- BAD: "Copy files in with a `COPY` step." The snapshot recipe uses `dockerfile_commands()` with no
110+
local build context, so `COPY` cannot work. `RUN git clone` can.
111+
- BAD: "Every other variable the runner reads is documented above." It reads about forty more.
112+
113+
If you cannot verify a claim, do not write it. Cut it, or go read the code.
114+
115+
### Prerequisites are the universal minimum; edge cases go in troubleshooting
116+
117+
A prerequisites list is what EVERY reader needs before they start. Do not pad it with the obvious,
118+
and do not front-load setup that only a subset hits. If a problem bites only some readers, put it in
119+
troubleshooting keyed to the symptom they will actually see, so the rest never read it.
120+
121+
- BAD (prerequisite): "Docker installed on your machine." Obvious. Cut it, or make it one line.
122+
- BAD (prerequisite): three paragraphs on `/var/run/docker.sock` ownership and `usermod -aG docker`
123+
before the reader can run anything. Most readers already have daemon access.
124+
- GOOD (troubleshooting): "**`permission denied ... /var/run/docker.sock`** — your user cannot reach
125+
the Docker daemon. Run as root, use `sudo`, or `sudo usermod -aG docker $USER` then open a new
126+
shell. (docker-group membership is root-equivalent on the host.)"
127+
128+
### Reuse the patterns that already exist
129+
130+
Before inventing a layout, look at how the rest of the docs do it. Use the existing Docusaurus components: `:::info`, `:::warning`, `:::tip`, collapsibles. Do not invent a new troubleshooting format per page. Consistency is what lets a reader skim.
131+
132+
Use an admonition for a genuine warning or aside. Do not smuggle a caveat into body prose.
133+
134+
### Titles are plain labels, not sentences
135+
136+
A heading is a signpost. "Sandbox isolation and security", not a clause or a claim. If a reader cannot tell what is under a heading from the heading alone, rename it.
137+
138+
### Overview pages are indexes, not essays
139+
140+
An overview answers "where do I start and what applies to me". It is a short, high-level map with links, not a prose introduction to the product and not a restatement of the sidebar. Assume the reader already knows what Agenta is.
141+
142+
### Comments in config examples are for humans
143+
144+
Env examples get commented for a human skimming them, not for exhaustive machine documentation.
145+
146+
- Comment the **non-obvious**: defaults, units, constraints, what breaks.
147+
- Do **not** comment the obvious. `AGENTA_RUNNER_CONCURRENCY_LIMIT` does not need "maximum concurrent runs".
148+
- Long explanations belong in the reference page, not in every env file.
149+
12150
## 1. Pick the doc type first (Diátaxis)
13151

14152
Before writing, decide which of the four types you are writing. Don't mix them in one page.

0 commit comments

Comments
 (0)