Skip to content

Commit e0ea130

Browse files
authored
Merge branch 'main' into feat/html-render-image-gallery
2 parents af96032 + 5211acf commit e0ea130

2,174 files changed

Lines changed: 97789 additions & 50405 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/test-t3-app/SKILL.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ Test with meaningful project and thread data. Read
2121
[references/sqlite-fixtures.md](references/sqlite-fixtures.md) only when
2222
inspecting or seeding SQLite. Stop the test server before direct fixture writes.
2323

24+
## Drive agent turns for free
25+
26+
Do not spend a paid model to see a turn render. `vp run dev` registers the
27+
`Replay (dev)` provider, which replays recorded turns with simulated streaming
28+
through the real adapter and orchestration. Its models are scenarios:
29+
`showcase` covers thinking, a plan, tool calls, an approval, and markdown.
30+
Press `Ctrl+Alt+Shift+R` for a picker (GNOME takes that key for screen
31+
recording), call `window.t3dev.replay("showcase")`
32+
with `preview_evaluate`, or run
33+
`node apps/server/src/bin.ts thread start --base-dir .t3 --provider devReplay --model showcase <project> "go"`.
34+
When the test needs real provider behavior, use that provider's cheapest
35+
model, such as Haiku for Claude or Luna for Codex.
36+
2437
## Use the Browser panel
2538

2639
Call `preview_status`, then `preview_open` if the Browser panel is

‎.coderabbit.config.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,19 @@ export default defineConfig((ctx) => ({
2626
path: "apps/web/src/**/*.{tsx,css}",
2727
instructions: "Hold changed code to the rules in docs/internals/web-ui.md.",
2828
},
29+
{
30+
path: "**/*.test.*",
31+
instructions:
32+
"Hold changed tests to the rules in docs/internals/testing.md. Request tests only as those rules allow.",
33+
},
2934
],
3035
},
3136
knowledge_base: {
3237
code_guidelines: {
3338
filePatterns: [
3439
{ files: "docs/internals/effect-services.md", applyTo: "{apps,packages,infra}/**/*.ts" },
3540
{ files: "docs/internals/web-ui.md", applyTo: "apps/web/src/**/*.{tsx,css}" },
41+
{ files: "docs/internals/testing.md", applyTo: "{apps,packages,infra}/**/*.{ts,tsx}" },
3642
],
3743
},
3844
},

‎.github/actions/setup-ci-deps/action.yml‎

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ inputs:
55
description: Read-only credential for the private maintainer task cache
66
required: false
77
default: ""
8+
install:
9+
description: Set to "false" to set up Vite+ only, with no pnpm store or install
10+
required: false
11+
default: "true"
12+
install-args:
13+
description: Arguments for `vp install`, such as filters. Empty installs the whole workspace.
14+
required: false
15+
default: ""
816
runs:
917
using: composite
1018
steps:
@@ -19,10 +27,11 @@ runs:
1927
echo "node=$(jq -r '.engines.node' package.json)" >> "$GITHUB_OUTPUT"
2028
echo "pnpm=$(jq -r '.packageManager' package.json)" >> "$GITHUB_OUTPUT"
2129
30+
# Pinned because release-desktop.yml signs builds with this action in the job.
2231
- name: Cache Vite+
2332
id: vp_cache
2433
if: steps.vp.outputs.version != ''
25-
uses: actions/cache@v6
34+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
2635
with:
2736
path: ~/.vite-plus
2837
key: vite-plus-home-v2-${{ runner.os }}-${{ runner.arch }}-${{ steps.vp.outputs.version }}-node${{ steps.vp.outputs.node }}-${{ steps.vp.outputs.pnpm }}
@@ -38,38 +47,93 @@ runs:
3847
echo "$HOME/.vite-plus/bin" >> "$GITHUB_PATH"
3948
"$HOME/.vite-plus/bin/vp" env use "${{ steps.vp.outputs.node }}"
4049
50+
# vp gives each Node.js download attempt 10 minutes by default, so one
51+
# stalled stream holds the job that long. It retries 3 times and resumes
52+
# partial bytes, so a shorter limit costs a slow download only a retry.
4153
- name: Setup Vite+
4254
if: steps.vp_cache.outputs.cache-hit != 'true'
43-
uses: voidzero-dev/setup-vp@v1
55+
uses: voidzero-dev/setup-vp@250f29ce396baf5e8f24498e17c0dfdebabc26eb # v1
56+
env:
57+
VP_DOWNLOAD_TIMEOUT: "120"
4458
with:
4559
node-version-file: package.json
4660
cache: false
4761
run-install: false
4862

49-
- name: Resolve pnpm store path
63+
# A shared key lets whichever install saves first, often a small filtered
64+
# one, become every job's store. Each set of install args gets its own.
65+
- name: Resolve pnpm store
5066
id: pnpm_store
67+
if: inputs.install == 'true'
5168
shell: bash
69+
env:
70+
INSTALL_ARGS: ${{ inputs.install-args }}
5271
run: |
53-
store_path="$(vp pm cache dir)"
54-
echo "path=$store_path" >> "$GITHUB_OUTPUT"
72+
echo "path=$(vp pm cache dir)" >> "$GITHUB_OUTPUT"
73+
metadata_dir="${pnpm_config_cache_dir:-$RUNNER_TEMP/pnpm-metadata}"
74+
echo "pnpm_config_cache_dir=$metadata_dir" >> "$GITHUB_ENV"
75+
echo "verified=$metadata_dir/lockfile-verified.jsonl" >> "$GITHUB_OUTPUT"
76+
if [[ -z "$INSTALL_ARGS" ]]; then
77+
echo "namespace=full" >> "$GITHUB_OUTPUT"
78+
else
79+
echo "namespace=filtered-$(printf '%s' "$INSTALL_ARGS" | cksum | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
80+
fi
5581
56-
# setup-vp's shared cache key ignores install filters. Keep full installs in a
57-
# separate namespace, including fallback restores.
82+
# Filtered installs prefer the current full store, which holds every package,
83+
# over an older store of their own.
5884
# The pnpm store stays a cache tarball on the runner's own disk. A store on a
5985
# Blacksmith sticky disk mounts in under a second, but pnpm then copies every
6086
# package across filesystems instead of hard-linking it: install took 28-49s
6187
# against 9s plus an 11s restore.
62-
- name: Cache full pnpm store
63-
uses: actions/cache@v6
88+
- name: Restore pnpm store
89+
id: pnpm_cache
90+
if: inputs.install == 'true'
91+
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
6492
with:
6593
path: ${{ steps.pnpm_store.outputs.path }}
66-
key: pnpm-full-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', 'package.json') }}
94+
key: pnpm-${{ steps.pnpm_store.outputs.namespace }}-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', 'package.json') }}
6795
restore-keys: |
96+
pnpm-full-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', 'package.json') }}
97+
pnpm-${{ steps.pnpm_store.outputs.namespace }}-v1-${{ runner.os }}-${{ runner.arch }}-
6898
pnpm-full-v1-${{ runner.os }}-${{ runner.arch }}-
6999
100+
# pnpm accepts an earlier pass for the same lockfile and policy at any age, so
101+
# a cached record skips fetching registry metadata for every package.
102+
- name: Restore lockfile verification
103+
id: pnpm_verified
104+
if: inputs.install == 'true'
105+
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
106+
with:
107+
path: ${{ steps.pnpm_store.outputs.verified }}
108+
key: pnpm-verified-v1-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', 'package.json') }}
109+
70110
- name: Install dependencies
111+
if: inputs.install == 'true'
71112
shell: bash
72-
run: vp install
113+
env:
114+
INSTALL_ARGS: ${{ inputs.install-args }}
115+
# Unquoted so each filter is its own argument.
116+
run: vp install $INSTALL_ARGS
117+
118+
- name: Save lockfile verification
119+
if: inputs.install == 'true' && steps.pnpm_verified.outputs.cache-hit != 'true'
120+
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
121+
with:
122+
path: ${{ steps.pnpm_store.outputs.verified }}
123+
key: ${{ steps.pnpm_verified.outputs.cache-primary-key }}
124+
125+
# A filtered install that started from the current full store adds nothing
126+
# worth a second copy of it.
127+
- name: Save pnpm store
128+
if: >-
129+
inputs.install == 'true' &&
130+
steps.pnpm_cache.outputs.cache-hit != 'true' &&
131+
(steps.pnpm_store.outputs.namespace == 'full' ||
132+
steps.pnpm_cache.outputs.cache-matched-key != format('pnpm-full-v1-{0}-{1}-{2}', runner.os, runner.arch, hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', 'package.json')))
133+
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
134+
with:
135+
path: ${{ steps.pnpm_store.outputs.path }}
136+
key: ${{ steps.pnpm_cache.outputs.cache-primary-key }}
73137

74138
- name: Enable shared task cache
75139
if: >-

‎.github/scripts/ci-changes.cjs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const RUST = /^native\/(resource-monitor|kde-snap-shot|hyprland-snap-shot)\//;
3232

3333
// These tests read other packages without declaring dependencies.
3434
const TEST_ONLY_DEPS = {
35+
// Evals import the server's source to test its real model-facing text.
36+
"@t3tools/evals": ["t3"],
3537
"@t3tools/provider-pi": ["@t3tools/client-runtime"],
3638
"@t3tools/scripts": [
3739
"@t3tools/desktop",

‎.github/triage/PLAYBOOK.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ provided alongside this playbook. Everything machine-specific lives there, not h
1111

1212
## 1. Ask what went wrong
1313

14-
Your first message to the user: ask them to describe what went wrong, in their own
15-
words. Ask them to paste screenshots directly into this session if they have any.
14+
Unless the user already described it, your first message asks what went wrong, in
15+
their own words. Ask them to paste screenshots directly into this session if they have any.
1616
Ask follow-up questions when the description is vague. Good repro steps are the most
1717
valuable thing you can extract from this conversation.
1818

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Agent Context Budget
2+
3+
# Fails when a PR raises a value in agentContextBudget.json above main's. AgentContextBudget.test.ts
4+
# keeps that file equal to the measured context, so the context cannot grow by editing the file.
5+
# To accept growth on purpose, a maintainer adds the agent-context-increase label.
6+
on:
7+
pull_request:
8+
types: [opened, synchronize, reopened, labeled, unlabeled]
9+
10+
permissions:
11+
contents: read
12+
13+
concurrency:
14+
group: agent-context-budget-${{ github.event.pull_request.number }}
15+
cancel-in-progress: true
16+
17+
jobs:
18+
budget:
19+
name: Agent context budget
20+
runs-on: ubuntu-24.04
21+
timeout-minutes: 5
22+
env:
23+
BUDGET: apps/server/src/provider/agentContextBudget.json
24+
ALLOW_INCREASE: ${{ contains(github.event.pull_request.labels.*.name, 'agent-context-increase') }}
25+
steps:
26+
- name: Checkout
27+
uses: actions/checkout@v6
28+
with:
29+
sparse-checkout: apps/server/src/provider/agentContextBudget.json
30+
sparse-checkout-cone-mode: false
31+
32+
- name: Compare with main
33+
run: |
34+
git fetch --quiet --depth=1 origin main
35+
if ! git cat-file -e "FETCH_HEAD:$BUDGET" 2>/dev/null; then
36+
echo "main has no agent context budget yet."
37+
exit 0
38+
fi
39+
git show "FETCH_HEAD:$BUDGET" > "$RUNNER_TEMP/main.json"
40+
grown=$(jq -r --slurpfile main "$RUNNER_TEMP/main.json" '
41+
to_entries[]
42+
| select($main[0][.key] != null and .value > $main[0][.key])
43+
| "\(.key): \($main[0][.key]) -> \(.value) (+\(.value - $main[0][.key]))"
44+
' "$BUDGET")
45+
if [ -z "$grown" ]; then
46+
echo "Agent context is not larger than main."
47+
exit 0
48+
fi
49+
printf 'Agent context grew compared to main:\n%s\n' "$grown"
50+
if [ "$ALLOW_INCREASE" = "true" ]; then
51+
echo "Accepted by the agent-context-increase label."
52+
exit 0
53+
fi
54+
echo "::error file=$BUDGET::Agent context grew compared to main. Cut the added text, or ask a maintainer to add the agent-context-increase label."
55+
exit 1

‎.github/workflows/ci.yml‎

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -225,16 +225,6 @@ jobs:
225225
!/apps/mobile/modules/t3-terminal/Vendor/**/*.a
226226
sparse-checkout-cone-mode: false
227227

228-
# Blacksmith boots GitHub's Ubuntu runner image (gcc is usually present),
229-
# but ACP process-tree live tests compile a small pthread fixture with `cc`
230-
# and soft-skip when it is missing. Install build-essential so that path
231-
# always runs in CI instead of silently no-oping.
232-
- name: Install C toolchain for process-tree fixtures
233-
run: |
234-
sudo apt-get update
235-
sudo apt-get install -y --no-install-recommends build-essential
236-
command -v cc
237-
238228
- uses: ./.github/actions/setup-apt-mirrors
239229

240230
# The desktop tests compile the browser secret helper. The install runs in
@@ -344,6 +334,12 @@ jobs:
344334
- name: Test
345335
run: vp run --filter @t3tools/web test
346336

337+
# Browser tests run source modules; this boots the production chunk graph.
338+
- name: Boot the built web client
339+
run: |
340+
vp run --filter @t3tools/web build
341+
node apps/web/scripts/verify-built-boot.ts
342+
347343
# apps/server sets `fileParallelism: false`, so its test files run strictly
348344
# one at a time. Sharding spreads them over separate runners instead of
349345
# separate workers, so no two server test files ever share a machine and the
@@ -358,7 +354,9 @@ jobs:
358354
id-token: write
359355
name: Test Server ${{ matrix.shard }}
360356
runs-on: blacksmith-4vcpu-ubuntu-2404
361-
timeout-minutes: 10
357+
# Shards take 2-5 minutes. When the shared task cache throttles reads, each
358+
# failed fetch stalls vp for 15-90s, which pushed shards past 10 minutes.
359+
timeout-minutes: 20
362360
strategy:
363361
fail-fast: false
364362
matrix:

‎.github/workflows/deploy-relay.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@ on:
55
branches:
66
- main
77
# Redeploys main after a variable or secret change. Alchemy redeploys the
8-
# Worker when a value it reads changes, so force is rarely needed, and it
9-
# replaces the Postgres runtime role (alchemy-run/alchemy#1832).
8+
# Worker when a value it reads changes, so force is rarely needed.
109
workflow_dispatch:
1110
inputs:
1211
force:
13-
description: Redeploy every resource, including ones with no detected changes. Replaces the Postgres runtime role.
12+
description: Redeploy every resource, including ones with no detected changes.
1413
type: boolean
1514
default: false
1615

@@ -81,6 +80,7 @@ jobs:
8180
CLERK_SECRET_KEY: ${{ secrets.CLERK_SECRET_KEY }}
8281
APNS_PRIVATE_KEY: ${{ secrets.APNS_PRIVATE_KEY }}
8382
FCM_SERVICE_ACCOUNT: ${{ secrets.FCM_SERVICE_ACCOUNT }}
83+
T3CHAT_LINK_SECRET: ${{ secrets.T3CHAT_LINK_SECRET }}
8484

8585
- name: Publish relay deploy commit status
8686
uses: actions/github-script@v8

‎.github/workflows/mobile-eas-production.yml‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ jobs:
275275
--environment production \
276276
--platform ${{ inputs.platform }} \
277277
--message "$OTA_MESSAGE" \
278+
--emit-metadata \
278279
--non-interactive
279280
280281
# Reconcile each version separately because the major is in the fingerprint.
@@ -313,8 +314,11 @@ jobs:
313314
--environment production \
314315
--platform "$platform" \
315316
--message "$message" \
317+
--emit-metadata \
316318
--non-interactive; then
317319
echo ":white_check_mark: $platform/$profile: OTA published to production (fingerprint \`$hash\`)" >> "$GITHUB_STEP_SUMMARY"
320+
# The next target's export replaces dist; keep this one for the map upload.
321+
rm -rf "dist-$platform-$profile" && mv dist "dist-$platform-$profile"
318322
else
319323
failed=1
320324
echo ":x: $platform/$profile: OTA publish failed (fingerprint \`$hash\`)" >> "$GITHUB_STEP_SUMMARY"
@@ -354,3 +358,18 @@ jobs:
354358
fi
355359
done
356360
exit "$failed"
361+
362+
# Lets the relay symbolicate crash stacks from the updates published
363+
# above. A step of its own so the R2 credentials reach only this script,
364+
# not `eas update` and the dependencies it runs. Never fails the job.
365+
- name: Upload OTA source maps
366+
if: ${{ !cancelled() && steps.expo-token.outputs.present == 'true' }}
367+
working-directory: apps/mobile
368+
env:
369+
MOBILE_SOURCEMAPS_R2: ${{ secrets.MOBILE_SOURCEMAPS_R2 }}
370+
run: |
371+
for dir in dist dist-*; do
372+
if [ -f "$dir/eas-update-metadata.json" ]; then
373+
node scripts/upload-sourcemaps.mts ota "$dir"
374+
fi
375+
done

‎.github/workflows/relay-preview.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,5 @@ jobs:
7979
PLANETSCALE_API_TOKEN: ${{ secrets.PLANETSCALE_API_TOKEN }}
8080
AXIOM_TOKEN: ${{ secrets.AXIOM_TOKEN }}
8181
CLERK_SECRET_KEY: ${{ secrets.CLERK_SECRET_KEY }}
82+
# Unset by default, so previews keep the T3 Chat link off.
83+
T3CHAT_LINK_SECRET: ${{ secrets.T3CHAT_LINK_SECRET }}

0 commit comments

Comments
 (0)