Skip to content
Merged
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
55 changes: 47 additions & 8 deletions .github/workflows/agent-commands.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
# are shorthands. Works on any command form and on the
# workflow_dispatch `model` input; default is the
# VERIFY_MODEL repo variable, else claude-opus-5.
# ... --retry re-run and UPDATE the thread's latest findings comment
# in place instead of posting a new one (GitHub keeps the
# edit history). If the thread has no findings comment
# yet, a new one is posted. Dispatch input: `retry`.
# ... --announce post the "started — watch the run" comment. Off by
# default; the eyes reaction is the only receipt.
# @expo-bot check verify + review from one comment. This workflow runs the
# verify half (--fix / --no-fix apply, issues and PRs);
# expo-code-review-command.yml picks up the review half
Expand Down Expand Up @@ -120,6 +126,10 @@ on:
description: "Override the agent model for this run (a full claude-* id, e.g. claude-fable-5). Empty = the VERIFY_MODEL repo variable, else claude-opus-5. Comment-path runs never set this."
required: false
type: string
retry:
description: "Update the thread's latest findings comment in place instead of posting a new one"
type: boolean
default: false

# Scopes the automatic workflow token, which is what the AGENT step gets:
# issue comments yes, repository contents never. The agent therefore cannot
Expand Down Expand Up @@ -336,6 +346,7 @@ jobs:
INPUT_FIX: ${{ inputs.fix }}
INPUT_MODE: ${{ inputs.mode }}
INPUT_MODEL: ${{ inputs.model }}
INPUT_RETRY: ${{ inputs.retry }}
REQUEST: ${{ github.event.comment.body || inputs.request }}
run: |
# Reject an ill-formed command. On the comment path the reason also
Expand Down Expand Up @@ -504,15 +515,33 @@ jobs:
;;
esac
fi
# The "started — watch the run" comment is opt-in: threads stay
# quiet unless the commander asks for the watch link with
# --announce. The eyes reaction remains the default receipt.
announce=false
if [ "$QUIET" != "true" ]; then
case " $line " in *" --announce "*) announce=true ;; esac
fi
# --retry: the publish phase passes updateLatest to the findings
# tool, so the thread's previous findings comment is edited in
# place rather than joined by a sibling.
retry=false
if [ "$QUIET" = "true" ]; then
if [ "$INPUT_RETRY" = "true" ]; then retry=true; fi
else
case " $line " in *" --retry "*) retry=true ;; esac
fi
{
# route last: everything before this line can still reject.
echo "mode=$mode"
echo "fix=$fix"
echo "is_pr=$IS_PR"
echo "model=$model"
echo "announce=$announce"
echo "retry=$retry"
echo "route=true"
} >> "$GITHUB_OUTPUT"
echo "mode: $mode; fix mode: $fix; model: ${model:-default} (target is $([ "$IS_PR" = true ] && echo 'a pull request' || echo 'an issue'))"
echo "mode: $mode; fix mode: $fix; model: ${model:-default}; announce: $announce; retry: $retry (target is $([ "$IS_PR" = true ] && echo 'a pull request' || echo 'an issue'))"

# A maintainer typed a command and it did not launch — say so where it
# was typed. Rejections used to surface two bad ways: a red step in a
Expand All @@ -533,11 +562,12 @@ jobs:
GH_TOKEN: ${{ secrets.EXPO_BOT_GITHUB_TOKEN }}
run: gh api -X POST "repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" -f content=eyes

# Watchable from the start: the Actions run streams the agent's live
# progress, so the thread gets that link the moment work begins (the
# Opt-in via --announce: the Actions run streams the agent's live
# progress, and this posts that link the moment work begins (the
# findings comment lands separately when the investigation completes).
# Default is no comment — the eyes reaction is the only receipt.
- name: Announce run
if: steps.gate.outputs.ok == 'true' && steps.cmd.outputs.route == 'true' && env.QUIET != 'true'
if: steps.gate.outputs.ok == 'true' && steps.cmd.outputs.route == 'true' && steps.cmd.outputs.announce == 'true' && env.QUIET != 'true'
env:
GH_TOKEN: ${{ secrets.EXPO_BOT_GITHUB_TOKEN }}
MODE: ${{ steps.cmd.outputs.mode }}
Expand All @@ -558,9 +588,9 @@ jobs:
# are in_progress too, so ordering by (created_at, id) IS the queue;
# admission by global position keeps the executing count at CAP even
# when several waiters poll at once. Runs pending on the per-target
# group are not in_progress and hold no capacity. The announce above
# already posted the watch link, so the queue wait is visible from
# the thread.
# group are not in_progress and hold no capacity. With --announce the
# thread has the watch link during this wait; by default the queue
# wait is visible only from the Actions tab.
- name: Wait for a free verification slot
if: steps.gate.outputs.ok == 'true' && steps.cmd.outputs.route == 'true'
env:
Expand Down Expand Up @@ -1352,6 +1382,7 @@ jobs:
CUT_SHORT: ${{ steps.investigate.outputs.cut_short }}
WRITES_DENIED: ${{ steps.investigate.outputs.writes_denied }}
MODE: ${{ steps.cmd.outputs.mode }}
RETRY: ${{ steps.cmd.outputs.retry }}
run: |
set -o pipefail
review=$(cat .verify-out/review.md)
Expand All @@ -1372,10 +1403,18 @@ jobs:
else
writes_denied=""
fi
# --retry: the maintainer asked this run to REPLACE the thread's
# previous findings comment rather than add a sibling. The server
# resolves which comment that is; the agent only sets the flag.
if [ "$RETRY" = "true" ]; then
retry_note="RETRY MODE: this thread already carries a findings comment from an earlier run, and the maintainer asked for it to be UPDATED IN PLACE. Pass updateLatest: true in your mcp__sandbox__github_comment_issue call; write the body as the complete, current findings (it replaces the old body entirely — do not write it as a diff against the previous comment). "
else
retry_note=""
fi
set +o pipefail
rc_file="$RUNNER_TEMP/revise.rc"
{ timeout 1800 claude -p --resume "$SESSION_ID" \
"${cut_short}${writes_denied}A reviewer read your draft and the log of this run WITHOUT seeing your reasoning, and tried to break it. Their review follows. Treat it as a colleague's objections, not as orders: answer every MUST-FIX, and where you disagree, say so in the outcome with your reason rather than silently ignoring it. Any sandbox you created is still alive — if an objection can be settled by measuring again, measure again instead of arguing. Drop any evidence they show supports no claim, and correct anything they show you asserted without checking.
"${cut_short}${writes_denied}${retry_note}A reviewer read your draft and the log of this run WITHOUT seeing your reasoning, and tried to break it. Their review follows. Treat it as a colleague's objections, not as orders: answer every MUST-FIX, and where you disagree, say so in the outcome with your reason rather than silently ignoring it. Any sandbox you created is still alive — if an objection can be settled by measuring again, measure again instead of arguing. Drop any evidence they show supports no claim, and correct anything they show you asserted without checking.

Then, and only then: post the outcome with mcp__sandbox__github_comment_issue; keep or withdraw .verify-out/pr.md and, in work mode, .verify-out/changes.json as the command mode requires; and destroy EVERY sandbox you created with mcp__sandbox__destroy_sandbox before you finish. Destroy them even if posting fails.

Expand Down
2 changes: 1 addition & 1 deletion docs/pages/eas/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: EAS CLI reference
sidebar_title: EAS CLI
description: EAS CLI is a command-line tool that allows you to interact with Expo Application Services (EAS) from your terminal.
cliVersion: 22.0.0
cliVersion: 22.2.0
---

import { EASCLIReference } from '~/ui/components/EASCLIReference';
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/eas/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Read the full pitch at [expo.dev/eas](https://expo.dev/eas), or follow the links
/>

<BoxLink
title="EAS Observe (in beta)"
title="EAS Observe"
description="Monitor your app's performance in production."
href="/eas/observe/introduction/"
Icon={ActivityIcon}
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/eas/observe/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { RelatedSkills } from '~/ui/components/RelatedSkills';
import { Terminal } from '~/ui/components/Snippet';
import { VideoBoxLink } from '~/ui/components/VideoBoxLink';

> **info** EAS Observe includes 10,000 monthly active users on the Free plan and 50,000 on paid plans. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details.
> **info** EAS Observe includes 100,000 events per month on the Free plan and 500,000 on paid plans, roughly 10,000 and 50,000 monthly active users. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details.

**EAS Observe** is a performance monitoring service from Expo for tracking how your app performs in production. It gives you visibility into real-world startup times, rendering performance, and user experience across different devices, networks, and conditions.

Expand Down
2 changes: 1 addition & 1 deletion docs/pages/versions/unversioned/sdk/observe.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ platforms: ['android', 'ios', 'tvos']
import APISection from '~/components/plugins/APISection';
import { APIInstallSection } from '~/components/plugins/InstallSection';

> **info** EAS Observe includes 10,000 monthly active users on the Free plan and 50,000 on paid plans. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details.
> **info** EAS Observe includes 100,000 events per month on the Free plan and 500,000 on paid plans, roughly 10,000 and 50,000 monthly active users. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details.

`expo-observe` is a library that collects performance metrics and user-defined events from your app and dispatches them to [EAS Observe](/eas/observe/introduction/), a performance monitoring service from Expo, or to your preferred OpenTelemetry (OTEL)-compliant backend. It measures real-world startup performance, such as [Time to First Render (TTR)](/eas/observe/reference/metrics/#time-to-first-render-ttr) and [Time to Interactive (TTI)](/eas/observe/reference/metrics/#time-to-interactive-tti), from apps running in production.

Expand Down
3 changes: 1 addition & 2 deletions docs/pages/versions/v55.0.0/sdk/observe.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ description: A library that collects app performance metrics and dispatches them
sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-55/packages/expo-observe'
packageName: 'expo-observe'
platforms: ['android', 'ios', 'tvos']
isBeta: true
---

import APISection from '~/components/plugins/APISection';
import { APIInstallSection } from '~/components/plugins/InstallSection';

> **important** **EAS Observe** is in [Open Beta](/more/release-statuses/#beta). The first 10,000 monthly active users are free. For higher usage, contact [sales@expo.dev](mailto:sales@expo.dev).
> **info** EAS Observe includes 100,000 events per month on the Free plan and 500,000 on paid plans, roughly 10,000 and 50,000 monthly active users. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details.

`expo-observe` is a library that collects performance metrics from your app and dispatches them to [EAS Observe](/eas/observe/introduction/), a performance monitoring service from Expo, or to your preferred OpenTelemetry (OTEL)-compliant backend. It measures real-world startup performance, such as [Time to First Render (TTR)](/eas/observe/reference/metrics/#time-to-first-render-ttr) and [Time to Interactive (TTI)](/eas/observe/reference/metrics/#time-to-interactive-tti), from apps running in production.

Expand Down
3 changes: 1 addition & 2 deletions docs/pages/versions/v56.0.0/sdk/observe.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ description: A library that collects app performance metrics and user-defined ev
sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-observe'
packageName: 'expo-observe'
platforms: ['android', 'ios', 'tvos']
isBeta: true
---

import APISection from '~/components/plugins/APISection';
import { APIInstallSection } from '~/components/plugins/InstallSection';

> **important** **EAS Observe** is in [Open Beta](/more/release-statuses/#beta). The first 10,000 monthly active users are free. For higher usage, contact [sales@expo.dev](mailto:sales@expo.dev).
> **info** EAS Observe includes 100,000 events per month on the Free plan and 500,000 on paid plans, roughly 10,000 and 50,000 monthly active users. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details.

`expo-observe` is a library that collects performance metrics and user-defined events from your app and dispatches them to [EAS Observe](/eas/observe/introduction/), a performance monitoring service from Expo, or to your preferred OpenTelemetry (OTEL)-compliant backend. It measures real-world startup performance, such as [Time to First Render (TTR)](/eas/observe/reference/metrics/#time-to-first-render-ttr) and [Time to Interactive (TTI)](/eas/observe/reference/metrics/#time-to-interactive-tti), from apps running in production.

Expand Down
2 changes: 1 addition & 1 deletion docs/pages/versions/v57.0.0/sdk/observe.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ platforms: ['android', 'ios', 'tvos']
import APISection from '~/components/plugins/APISection';
import { APIInstallSection } from '~/components/plugins/InstallSection';

> **info** EAS Observe includes 10,000 monthly active users on the Free plan and 50,000 on paid plans. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details.
> **info** EAS Observe includes 100,000 events per month on the Free plan and 500,000 on paid plans, roughly 10,000 and 50,000 monthly active users. Beyond that, pricing is usage-based. See [Pricing](https://expo.dev/pricing) for details.

`expo-observe` is a library that collects performance metrics and user-defined events from your app and dispatches them to [EAS Observe](/eas/observe/introduction/), a performance monitoring service from Expo, or to your preferred OpenTelemetry (OTEL)-compliant backend. It measures real-world startup performance, such as [Time to First Render (TTR)](/eas/observe/reference/metrics/#time-to-first-render-ttr) and [Time to Interactive (TTI)](/eas/observe/reference/metrics/#time-to-interactive-tti), from apps running in production.

Expand Down
19 changes: 13 additions & 6 deletions docs/scripts/generate-llms/llms-txt.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,20 @@ function processPage(page) {
return processPageData(page.href, page.name);
}

function processGroup(group) {
const items = (group.children ?? [])
.filter(child => child.type === 'page')
.map(processPage)
function collectPages(node) {
return (node.children ?? [])
.flatMap(child => (child.type === 'page' ? processPage(child) : collectPages(child)))
.filter(Boolean);
}

function processGroup(group) {
const items = collectPages(group);

return items.length > 0 ? { title: group.name, items } : null;
}

function hasContent(section) {
return section?.items?.length ?? section?.groups?.length ?? section?.sections?.length;
return section?.items.length > 0 || section?.groups.length > 0 || section?.sections.length > 0;
}

const COLLAPSED_SECTIONS = new Set(['Expo UI']);
Expand All @@ -161,6 +164,10 @@ function collapseToOverviews(section) {
return { ...section, items: [...section.items, ...overviews], groups: [], sections: [] };
}

function processNestedSection(node) {
return { title: node.name, items: collectPages(node), groups: [], sections: [] };
}

function processSection(node) {
if (!node || node.type !== 'section') {
return null;
Expand Down Expand Up @@ -190,7 +197,7 @@ function processSection(node) {
break;
}
case 'section': {
const sectionData = processSection(child);
const sectionData = processNestedSection(child);
if (hasContent(sectionData)) {
section.sections.push(sectionData);
}
Expand Down
Loading
Loading