Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
a029686
feat(flags): developer-flags backend foundation (#1506, ADR 0068 slic…
mabry1985 Jul 1, 2026
3c1b413
test(config): derive config-surface goldens from FIELDS (one value ga…
mabry1985 Jul 1, 2026
2195782
feat(flags): GET /api/flags operator route (#1506, ADR 0068 slice 2) …
mabry1985 Jul 1, 2026
33cb2c3
feat(flags): Developer panel + useFlag hook (#1506, ADR 0068 slices 3…
mabry1985 Jul 1, 2026
4e12fe3
test(flags): fail CI on a flag past its remove_by (#1506, ADR 0068 sl…
mabry1985 Jul 1, 2026
7a2b364
fix(chat): disable streamdown line numbers — they render as a broken …
mabry1985 Jul 1, 2026
bfb5b62
docs(flags): developer-flags how-to guide (#1506, ADR 0068 slice 6a) …
mabry1985 Jul 1, 2026
8225754
ci(desktop): build on manual dispatch only, not every tag (#1547)
mabry1985 Jul 1, 2026
a1e8d98
fix(wait): humanize wait/yield tool output so agents don't echo raw s…
mabry1985 Jul 1, 2026
059af47
docs(proto): fully-isolate throwaway test servers (own box root), not…
mabry1985 Jul 1, 2026
72c600e
fix(fleet): recover gracefully when the focused agent is down instead…
mabry1985 Jul 1, 2026
e238c17
feat(chat): slash-command UX — mid-input trigger, popover auto-scroll…
mabry1985 Jul 1, 2026
3fdb687
feat(chat): Cmd/Ctrl+O toggles the latest tool-call block (#1526) (#1…
mabry1985 Jul 1, 2026
afd85da
feat(web): adopt @protolabsai/ui 0.53.0 — drop 3 DS workarounds (#1546)
mabry1985 Jul 1, 2026
905e173
feat(console): header brand settings menu + unified settings sub-pane…
mabry1985 Jul 1, 2026
3e761db
docs(changelog): record the quick-wins batch + desktop-build + wait-t…
mabry1985 Jul 1, 2026
e9e9048
revert(console): drop brand-mark settings menu; fleet switcher always…
mabry1985 Jul 1, 2026
f3675c0
feat(chat): /compact — summarize + archive a thread, rewrite the chec…
mabry1985 Jul 1, 2026
c3367a2
docs(changelog): /compact + the always-on agent switcher (round 2) (#…
mabry1985 Jul 1, 2026
0406713
fix(issue-template): drop expected-vs-actual, make acceptance optiona…
mabry1985 Jul 1, 2026
1fb90ed
chore: release v0.78.0 (#1563)
mabry1985 Jul 1, 2026
6bdd129
fix(console-dev): default vite proxy to the isolated :7871 dev instan…
mabry1985 Jul 1, 2026
9aa019a
test(compaction): prove /compact's archive is real + searchable end-t…
mabry1985 Jul 1, 2026
403ca3e
feat(fleet): data-driven archetype registry + apply persona on create…
mabry1985 Jul 1, 2026
ee70a71
fix(paths): colocation warning keys on instance_root, not the shared …
mabry1985 Jul 1, 2026
8985374
fix(setup-wizard): surface bundle-install result via toast + fall bac…
mabry1985 Jul 1, 2026
c479854
feat(plugins): rail context menu — version, update-if-available, unin…
mabry1985 Jul 1, 2026
44a4d75
feat(marketing): data-driven /roadmap page from ROADMAP.md (#1532) (#…
mabry1985 Jul 1, 2026
a6d5787
feat(chat): "Rewind to here" — truncate a thread at a message, rewrit…
mabry1985 Jul 1, 2026
417141d
feat(console): live-update the Work panel Overview on goal/task/sched…
mabry1985 Jul 1, 2026
f9b79ed
fix(plugins): git clone --no-hardlinks so local-path installs don't f…
mabry1985 Jul 1, 2026
24f443f
fix(plugins): a stale untracked live copy can't shadow a newer bundle…
mabry1985 Jul 1, 2026
e4625f2
fix(artifact): pointer-lock in nested iframe, crisp fit-to-window, pe…
mabry1985 Jul 1, 2026
e5d3e04
feat(install): one-command curl|sh installer with a parity CLI wizard…
mabry1985 Jul 1, 2026
41e9bf0
feat(memory): provenance stamping (fact→source session) + cited memor…
mabry1985 Jul 1, 2026
af0b74a
fix(chat): no shared api-default session, no unknown.json pooling, un…
mabry1985 Jul 1, 2026
576f4b1
feat(memory): attributed <prior_sessions> digest + recall_session + u…
mabry1985 Jul 1, 2026
8ad0007
fix(install): bind-aware health check + auth gate for non-loopback bi…
mabry1985 Jul 1, 2026
9c7a2b4
docs(memory): de-link ADR 0069 in starter-tools until the ADR doc mer…
mabry1985 Jul 1, 2026
870bbaf
docs(adr): ADR 0069 — memory delivery layer (attributed digest, prove…
mabry1985 Jul 1, 2026
4a0e4ea
ci(docs): run the docs build on every PR so it can gate merges (#1586)
mabry1985 Jul 1, 2026
766279b
docs: restore ADR 0069 link in starter-tools (ADR merged in #1577) (#…
mabry1985 Jul 1, 2026
5adf42b
fix(chat): non-streaming turn must hold the per-thread lock on the sh…
mabry1985 Jul 1, 2026
f0ffdca
docs(changelog): ADR 0069 Round 1 entries under [Unreleased] (#1588)
mabry1985 Jul 1, 2026
9db7433
feat(operator-api): memory inspector REST — session summaries + hot m…
mabry1985 Jul 1, 2026
89080a1
feat(memory): namespace-scoped injection, incognito threads, per-turn…
mabry1985 Jul 1, 2026
01c20bb
chore(ci): make workflows fork-friendly — guard org-specific/expensiv…
mabry1985 Jul 1, 2026
99da035
feat(knowledge): collapsible source grouping (#1575) + Shift+click qu…
mabry1985 Jul 1, 2026
2bec346
feat(knowledge): supersede-don't-delete staleness — invalidated_at + …
mabry1985 Jul 1, 2026
a85f1dd
feat(knowledge): trust-tiered injection + hot-memory write visibility…
mabry1985 Jul 1, 2026
4635d1c
fix(chat): code-block header gap, lighter well colour, and no panel-s…
mabry1985 Jul 1, 2026
20bd38b
docs: true-up memory-hardening tasklist — all lanes shipped, follow-u…
mabry1985 Jul 1, 2026
737496d
feat(console): memory inspector surface + incognito threads (ADR 0069…
mabry1985 Jul 1, 2026
e91a43e
feat(evals): memory-regression probes — knowledge-update, abstention,…
mabry1985 Jul 1, 2026
02ae772
docs(adr): ADR 0070 — background results: push-resume, indexed report…
mabry1985 Jul 1, 2026
00a6c5c
chore: release v0.79.0 (#1602)
mabry1985 Jul 1, 2026
ef18bc4
fix(fleet): frozen-sidecar member spawn + surface boot failures (ADR …
mabry1985 Jul 2, 2026
29134c7
feat(background): push-resume to origin session, KB-indexed reports, …
mabry1985 Jul 2, 2026
2060862
feat(flags): put /compact behind the chat.compact developer flag (ADR…
mabry1985 Jul 2, 2026
ca0cbc8
feat(console): background report card — raised surface, clamped excer…
mabry1985 Jul 2, 2026
91c211a
fix(fleet): close two remote-member proxy-auth gaps (ADR 0042 §I) (#1…
mabry1985 Jul 2, 2026
baeaf02
feat(fleet): manual add-remote form (URL + token) + honest remote sta…
mabry1985 Jul 2, 2026
5cd2770
feat(fleet): edit remote members in place + recovery for down/mis-tok…
mabry1985 Jul 2, 2026
8b6ecf8
docs(adr-0042): true-up the reverse proxy + remote-member sections fo…
mabry1985 Jul 2, 2026
71e27ed
fix(tools): tools.disabled covers the full toolset + per-agent run_co…
mabry1985 Jul 2, 2026
91b67a9
feat(console): Work surface — card-first overview navigation, live ca…
mabry1985 Jul 2, 2026
d2ed18d
chore: release v0.80.0 (#1613)
mabry1985 Jul 2, 2026
7ff8bbc
feat(release): in-app update notes reuse the LLM changelog posted to …
mabry1985 Jul 2, 2026
57567ef
fix(console): memory tabs take an auto grid row + drop work-card titl…
mabry1985 Jul 2, 2026
1728e4e
feat(chat): container-responsive markdown tables (mobile-first) (#1616)
mabry1985 Jul 2, 2026
30fba9b
fix(tests): local-path plugin clones use file:// — kill the git local…
mabry1985 Jul 2, 2026
a7337c1
fix(chat): code blocks AND tables stack + stay responsive (streamdown…
mabry1985 Jul 2, 2026
a7ca02a
fix(chat): Stop cancels re-attached turns — durable task-id fallback …
mabry1985 Jul 2, 2026
88f88e8
fix(hitl): hold queued operator messages until form resolves (#1622)
mabry1985 Jul 2, 2026
bfd14a0
chore: move seccomp-profile.json into deploy/ (#1625)
mabry1985 Jul 2, 2026
fd344fd
chore(deps): bump @protolabsai/ui to 0.53.1, retire the redundant mar…
mabry1985 Jul 2, 2026
6371a92
chore: release v0.81.0 (#1627)
mabry1985 Jul 2, 2026
ba6a2bc
fix(release): bump release-tools pin to v2.4.0 — the out-file input e…
mabry1985 Jul 2, 2026
8acb266
fix(settings): QuickSetting dialogs — full-width helper text + cap th…
mabry1985 Jul 2, 2026
d96172a
feat(tools): per-row on/off toggles in the Tools panel (denylist edit…
mabry1985 Jul 2, 2026
bd64829
Merge remote-tracking branch 'upstream/main' into chore/sync-upstream…
mabry1985 Jul 2, 2026
34fcc19
test: true-up config-roundtrip golden to roxy's example config
mabry1985 Jul 2, 2026
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
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ node_modules/

# Seccomp profile — Docker reads this on the host at runtime
# (via docker-compose security_opt), NOT from inside the image
seccomp-profile.json
deploy/seccomp-profile.json

# OS junk
.DS_Store
Expand Down
9 changes: 1 addition & 8 deletions .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,13 @@ body:
description: Minimal steps, logs, or a stack trace that demonstrate it.
validations:
required: true
- type: textarea
id: expected-actual
attributes:
label: Expected vs. actual
description: What you expected to happen, and what actually happened.
validations:
required: true
- type: textarea
id: acceptance
attributes:
label: Acceptance
description: How we'll know it's fixed — verifiable criteria.
validations:
required: true
required: false
- type: input
id: env
attributes:
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ jobs:
with:
node-version: "20"
- name: Verify workspace config (.beads / .automaker / owned runners)
# Fork-friendly (#1534): the .beads/.automaker/owned-runner baseline is the
# protoLabs *fleet* standard, so it runs across the fleet (protoLabsAI/*) —
# keeping gina/protoTrader/roxy conformant — but is skipped on external forks,
# which are free to diverge. The job stays green either way (the server.py
# guard below still runs everywhere).
if: startsWith(github.repository, 'protoLabsAI/')
# The npm package is still 1.0.0 (predates verify-workspace-config),
# so run the script directly. It's zero-dependency (node builtins
# only), so no `npm install` is needed. Switch to
Expand Down
101 changes: 58 additions & 43 deletions .github/workflows/desktop-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,35 @@ name: Desktop Build
# updater bundles, and the updater-manifest fan-in job composes latest.json
# (the manifest the in-app updater polls) onto the release — uploaded last.
#
# Fires on EVERY semver tag push (patch included) alongside release.yml's Docker build,
# and on manual dispatch. Patches used to be skipped to save CI minutes, but this repo is
# public so GitHub-hosted runners (incl. the 10×/2× macOS/Windows legs) are free +
# unlimited — so every release, patch or minor, ships fresh desktop binaries and an
# updated latest.json (no more hand-attaching a patch's binaries). macOS builds are signed
# only when the full Apple secret set is present; Linux/Windows are always unsigned. See
# ADR 0010 (UI tiers / desktop).
# Runs on MANUAL DISPATCH ONLY (workflow_dispatch) — deliberately NOT on tag pushes.
# The macOS (10×) and Windows (2×) legs are this repo's only paid CI, and building the
# full matrix on EVERY semver tag (dozens/month at current release cadence) was the
# dominant cost — so desktop drops are now on-demand instead of per-release. Tag pushes
# still ship the Docker image + a (non-Latest) GitHub Release via release.yml; only the
# desktop binaries wait for a dispatch.
#
# Two dispatch modes, keyed off the `tag` input:
# • WITH a `tag` (a vX.Y.Z) → a real desktop RELEASE: binaries attach to that GitHub
# Release, the fan-in composes latest.json, and the release is promoted to 'Latest'
# (release.yml creates releases --latest=false; the promotion happens here). This is
# how a desktop update reaches users' in-app updater.
# • WITHOUT a tag (dispatched from a branch) → a TEST build: bundles upload as workflow
# artifacts only; no release, no latest.json, no 'Latest' change.
#
# macOS builds are signed only when the full Apple secret set is present; Linux/Windows
# are always unsigned. The repo guard keeps forks from running the matrix. See ADR 0010
# (UI tiers / desktop) and docs/guides/releasing.md § Desktop.

on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
tag:
description: 'Tag/ref to build against (blank = current ref)'
description: 'Tag to publish a desktop release for (vX.Y.Z). Blank = test build (artifacts only, no release).'
required: false

concurrency:
group: desktop-build-${{ github.ref }}
# Keyed on the dispatched tag (or ref) so two release dispatches don't collide.
group: desktop-build-${{ inputs.tag || github.ref }}
cancel-in-progress: false

permissions:
Expand All @@ -47,12 +56,11 @@ permissions:
jobs:
build:
name: ${{ matrix.target }}
# Build on every semver tag push (patch included) + any manual dispatch. The old
# patch-skip was a CI-cost guard; it's unnecessary on a public repo where GH-hosted
# runners are free, and it meant patch desktop fixes never reached users (their
# binaries weren't attached and latest.json kept pointing at the last minor). The
# repo guard keeps forks from running the matrix.
if: github.repository == 'protoLabsAI/protoAgent'
# Manual dispatch only (see the `on:` block) — the tag-push trigger was retired
# because the macOS/Windows matrix on every release was the repo's dominant CI cost.
# Fork-friendly (#1534): a fork can dispatch its own (unsigned) build; the repo
# clause keeps forks from auto-building should a tag trigger ever return.
if: github.repository == 'protoLabsAI/protoAgent' || github.event_name == 'workflow_dispatch'
# Per-leg runner, overridable by repo variable so the expensive macOS/Windows
# legs can move off GitHub-hosted runners (10×/2× billing multipliers) onto
# Namespace profiles WITHOUT editing this file — the moment the org provisions
Expand Down Expand Up @@ -199,8 +207,8 @@ jobs:
SIGNED="unsigned (dev)"
if [ "${RUNNER_OS}" = "macOS" ]; then
# tauri-bundler signs as soon as *any* Apple secret is set, then fails
# if the set is incomplete. Require the full set; otherwise unset all so
# a dispatch build falls back cleanly to unsigned.
# if the set is incomplete. Require the full set for a tagged (publish)
# dispatch; a test build (no tag) unsets all and falls back to unsigned.
if [ -n "${APPLE_CERTIFICATE}" ] && [ -n "${APPLE_CERTIFICATE_PASSWORD}" ] \
&& [ -n "${APPLE_SIGNING_IDENTITY}" ] && [ -n "${APPLE_API_ISSUER}" ] \
&& [ -n "${APPLE_API_KEY}" ] && [ -n "${APPLE_API_KEY_BASE64}" ] \
Expand All @@ -210,8 +218,8 @@ jobs:
python3 -c 'import base64,os,sys; open(sys.argv[1],"wb").write(base64.b64decode(os.environ["APPLE_API_KEY_BASE64"]))' "$KEYFILE"
export APPLE_API_KEY_PATH="${KEYFILE}"
SIGNED="Developer ID (signed + notarized)"
elif [ "${{ github.event_name }}" = "push" ]; then
echo "::error::Semver-tag desktop releases require the full Apple signing secret set."
elif [ -n "${{ inputs.tag }}" ]; then
echo "::error::A tagged desktop release (tag=${{ inputs.tag }}) requires the full Apple signing secret set."
exit 2
else
unset APPLE_CERTIFICATE APPLE_CERTIFICATE_PASSWORD APPLE_SIGNING_IDENTITY \
Expand Down Expand Up @@ -335,45 +343,45 @@ jobs:
if [ "${{ steps.build.outputs.signed }}" = "true" ]; then FLAG="--require-signed"; fi
scripts/verify-macos-desktop.sh $FLAG dist-desktop/*.dmg

- name: Upload dispatch artifact
if: github.event_name == 'workflow_dispatch'
- name: Upload test-build artifact
# No tag → a test build: bundles are downloadable workflow artifacts only.
if: inputs.tag == ''
uses: actions/upload-artifact@v6
with:
name: protoAgent-${{ steps.version.outputs.version }}-${{ matrix.target }}
path: dist-desktop/*

- name: Attach to release
if: github.event_name == 'push'
# A tag → publish: attach binaries to that GitHub Release (fan-in adds latest.json).
if: inputs.tag != ''
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload "${{ steps.version.outputs.tag }}" dist-desktop/* --clobber

# Fan-in: compose latest.json from every leg's signed updater bundle and
# upload it LAST, so the manifest never points at assets that don't exist
# yet. Runs only on semver tags, and only once ALL legs succeeded (a partial
# manifest would skew versions across platforms). If the release carries no
# updater bundles (key wasn't present), it skips — in-app updates are simply
# disabled for that release.
# yet. Runs only for a tagged (publish) dispatch, and only once ALL legs
# succeeded (a partial manifest would skew versions across platforms). If the
# release carries no updater bundles (key wasn't present), it skips — in-app
# updates are simply disabled for that release.
updater-manifest:
name: updater manifest (latest.json)
needs: build
# Runs on every tag push (patch included) so the in-app updater's latest.json is
# refreshed for every release. Gated to `push` so a manual dispatch (which builds
# artifacts, with no release to attach to) doesn't try to compose a manifest;
# `needs: build` ties it to a successful matrix.
# Only for a publish dispatch (a `tag` was supplied) — a test build (no tag) has no
# release to attach a manifest to. `needs: build` ties it to a successful matrix.
if: >-
github.event_name == 'push' && github.repository == 'protoLabsAI/protoAgent'
inputs.tag != '' && github.repository == 'protoLabsAI/protoAgent'
runs-on: ubuntu-latest # workspace-config: allow-hosted-runner public repo — GH-hosted is free
timeout-minutes: 10
steps:
# Checkout at the tag so we can read the rolled CHANGELOG.md section for the
# in-app updater notes (below). By tag-push time CHANGELOG.md already holds the
# in-app updater notes (below). By publish time CHANGELOG.md already holds the
# dated `## [VERSION]` section — the bump PR (prepare-release.yml) merged before
# the tag. Without this the job has no working tree (it only `gh release`s).
- uses: actions/checkout@v5
with:
ref: ${{ github.ref_name }}
ref: ${{ inputs.tag }}

- name: Setup Node
uses: actions/setup-node@v5
Expand All @@ -386,7 +394,7 @@ jobs:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
TAG="${{ github.ref_name }}"
TAG="${{ inputs.tag }}"
VERSION="${TAG#v}"
mkdir -p updater-dist
gh release download "$TAG" --repo "${{ github.repository }}" --dir updater-dist \
Expand All @@ -397,12 +405,19 @@ jobs:
exit 0
fi
# Updater notes, best → worst source:
# 1. The curated CHANGELOG.md section for this version (the human-written
# "### Added/Changed/Fixed" markdown) — what the in-app UpdateNotice renders.
# 2. The GitHub Release body (auto-generated commit subjects) — used when the
# CHANGELOG section is empty (e.g. a patch with no [Unreleased] bullets).
# 3. A generic placeholder, if both are empty.
NOTES="$(python3 scripts/changelog.py notes "$VERSION" 2>/dev/null || true)"
# 1. The LLM-themed notes posted to Discord — release.yml uploads them as the
# `release-notes.md` asset, so the in-app UpdateNotice renders the SAME text as
# the Discord embed (one changelog, one voice — #1516).
# 2. The curated CHANGELOG.md section for this version (the human-written
# "### Added/Changed/Fixed" markdown) — used when the LLM step didn't run
# (e.g. a fork with no gateway key, or the Discord step failed).
# 3. The GitHub Release body (auto-generated commit subjects) — if both are empty.
# 4. A generic placeholder, if all are empty.
NOTES=""
if gh release download "$TAG" --repo "${{ github.repository }}" -p release-notes.md -D "$RUNNER_TEMP" --clobber 2>/dev/null; then
NOTES="$(cat "$RUNNER_TEMP/release-notes.md" 2>/dev/null || true)"
fi
[ -z "$NOTES" ] && NOTES="$(python3 scripts/changelog.py notes "$VERSION" 2>/dev/null || true)"
[ -z "$NOTES" ] && NOTES="$(gh release view "$TAG" --repo "${{ github.repository }}" --json body -q .body 2>/dev/null || true)"
[ -z "$NOTES" ] && NOTES="protoAgent ${TAG} — see the GitHub Release for details."
npx --yes -p '@protolabsai/release-tools@latest' build-updater-manifest \
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@ permissions:
env:
REGISTRY: ghcr.io
# Normalise the image name — GHCR paths are case-sensitive and Docker refuses
# uppercase; github.repository would otherwise yield a mixed-case value.
IMAGE_NAME: protolabsai/protoagent
# uppercase; github.repository would otherwise yield a mixed-case value. A fork
# that opts in (see the job guard) sets its own `IMAGE_NAME` repo variable.
IMAGE_NAME: ${{ vars.IMAGE_NAME || 'protolabsai/protoagent' }}

jobs:
build-and-push:
# Fork-friendly (#1534): the hardcoded default pushes to protoLabs' GHCR, which
# forks can't write to. Only the canonical repo runs by default; a fork opts in
# by setting the `DOCKER_PUBLISH_ENABLED` repo variable (+ its own `IMAGE_NAME`).
if: github.repository == 'protoLabsAI/protoAgent' || vars.DOCKER_PUBLISH_ENABLED == 'true'
runs-on: ubuntu-latest # workspace-config: allow-hosted-runner public repo — GH-hosted is free
timeout-minutes: 30

Expand Down
12 changes: 9 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ on:
# PR-level gate (#976): vitepress fails the build on parse errors AND dead links,
# so running docs:build here catches doc breakage at review time instead of letting
# it merge green and only go red on the main-only deploy. Build-only — no deploy.
# Unfiltered on purpose: `build` is a REQUIRED check in the main ruleset, and a
# path-filtered required check never reports on non-matching PRs, blocking them
# forever. Running everywhere (~30s) is the price of making red docs unmergeable
# (a dead link once automerged and froze the Pages deploy on every main push).
pull_request:
paths: ['docs/**', 'package.json', 'package-lock.json', '.github/workflows/docs.yml']
workflow_dispatch:

permissions:
Expand Down Expand Up @@ -41,8 +44,11 @@ jobs:

deploy:
needs: build
# Never deploy from a PR — PRs only validate the build above.
if: github.event_name != 'pull_request'
# Never deploy from a PR — PRs only validate the build above. Fork-friendly
# (#1534): deploys to protoLabs' GitHub Pages, so only the canonical repo runs
# it — a fork without Pages configured would otherwise get a deploy error. The
# `build` job above is unguarded on purpose: forks want it (catches VitePress breakage).
if: github.event_name != 'pull_request' && github.repository == 'protoLabsAI/protoAgent'
runs-on: ubuntu-latest # workspace-config: allow-hosted-runner public repo — GH-hosted is free
environment:
name: github-pages
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/marketing-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ concurrency:
jobs:
deploy:
name: Build & deploy to Cloudflare Pages
# Fork-friendly (#1534): deploys to protoLabs' Cloudflare Pages project and needs
# CLOUDFLARE_* secrets no fork has — so only the canonical repo runs it.
if: github.repository == 'protoLabsAI/protoAgent'
runs-on: ubuntu-latest # workspace-config: allow-hosted-runner public repo — GH-hosted is free
steps:
- uses: actions/checkout@v5
Expand Down
36 changes: 28 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ on:

env:
REGISTRY: ghcr.io
IMAGE_NAME: protolabsai/protoagent
# Overridable (#1534): a fork that opts into RELEASE_ENABLED sets its own
# `IMAGE_NAME` repo variable to push to its GHCR instead of protoLabs'.
IMAGE_NAME: ${{ vars.IMAGE_NAME || 'protolabsai/protoagent' }}

jobs:
release:
Expand Down Expand Up @@ -145,13 +147,14 @@ jobs:
NOTES: ${{ steps.notes.outputs.notes }}
run: |
printf '%s' "$NOTES" > "$RUNNER_TEMP/release-notes.md"
# --latest=false: do NOT promote to 'Latest' yet. The in-app updater fetches
# --latest=false: do NOT promote to 'Latest'. The in-app updater fetches
# releases/latest/download/latest.json, so a release must not become 'Latest'
# until its latest.json exists — otherwise update checks 404 in the window
# before the desktop build's fan-in uploads it. The desktop fan-in flips this
# release to 'Latest' once latest.json is up (desktop-build.yml). Patch tags
# skip the desktop build entirely, so they stay non-Latest and 'Latest'
# correctly remains on the last .0 (the one carrying latest.json).
# until its latest.json exists — otherwise update checks 404. Desktop builds
# are now MANUAL (desktop-build.yml runs on workflow_dispatch, not on tags), so
# every tagged release stays non-Latest here; it's promoted to 'Latest' only
# when someone dispatches a desktop build for that tag and its fan-in uploads
# latest.json. 'Latest' therefore tracks the last DESKTOP release (the one the
# updater can actually use), not the newest tag.
gh release create "${{ steps.version.outputs.tag }}" \
--title "${{ steps.version.outputs.tag }}" \
--notes-file "$RUNNER_TEMP/release-notes.md" \
Expand All @@ -164,11 +167,28 @@ jobs:
# scripts/post-release-notes.mjs.

- name: Generate + post release notes
id: relnotes
continue-on-error: true
uses: protoLabsAI/release-tools@b7c3531dd67accb57ca09242f47d8c0eb1ace8eb # v1.1.0
uses: protoLabsAI/release-tools@d6bfc09ce7d17da3f196f3b54afde5830b8a6398 # v2.4.0 — first version with the out-file input (#1516/#1615)
with:
version: ${{ steps.version.outputs.tag }}
previous-version: ${{ steps.version.outputs.prev_tag }}
# Persist the SAME themed notes it posts to Discord (one generation writes the file
# AND posts the embed), so the in-app updater can render the identical text — one
# changelog, one voice (#1516). Uploaded as a release asset below; the (later,
# manual) desktop build prefers it for latest.json.
out-file: release-notes.md
env:
GATEWAY_API_KEY: ${{ secrets.GATEWAY_API_KEY }}
DISCORD_RELEASE_WEBHOOK: ${{ secrets.DISCORD_RELEASE_WEBHOOK }}

# Attach the LLM notes to the release so desktop-build.yml's latest.json fan-in can read
# them (it runs on a later manual dispatch, so the asset must live on the release, not a
# step output). Best-effort + guarded: a fork with no GATEWAY_API_KEY writes no file, the
# upload is skipped, and the updater falls back to the curated CHANGELOG.md section.
- name: Persist LLM release notes as an asset
if: hashFiles('release-notes.md') != ''
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload "${{ steps.version.outputs.tag }}" release-notes.md --clobber
Loading
Loading