Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
89aa4fa
docs: design of record for the local engine API (#44)
its-janghoon Sep 22, 2026
e152b21
feat(server): OpenAI-compatible /v1/chat/completions on the engine (#58)
its-janghoon Sep 22, 2026
be693b7
docs: correct three recommendations that research overturned (#60)
its-janghoon Sep 22, 2026
ec8ae0a
feat(protocol): document the chat-completions SSE response (#61)
its-janghoon Sep 23, 2026
b7bc394
feat(skill): K-1 code-mode domain globals and K-2 skill auto-arming (…
its-janghoon Oct 2, 2026
d72c759
K-3 document toolchain in code mode, and K-5 a skill that writes skil…
its-janghoon Oct 2, 2026
9fd8e87
PA-10 engine side: resolve `page` through a browser request channel
its-janghoon Oct 2, 2026
5d1e42e
fix(PA-10): exercise the four browser-request routes and name the thr…
its-janghoon Oct 3, 2026
9f98482
feat(PA-9): call the arming matcher, and carry the block it needs thr…
its-janghoon Oct 3, 2026
9f4689d
feat(PA-8): judge whether this machine can run a local model, and say…
its-janghoon Oct 3, 2026
5cc2163
feat(PA-8): a command that says what this machine can run, and fixes …
its-janghoon Oct 3, 2026
cc54ff7
Merge pull request #64 from redrob-labs/feature/pa10-page-over-the-br…
its-janghoon Oct 3, 2026
f8c7752
Merge pull request #65 from redrob-labs/feature/pa8-local-model-fit
its-janghoon Oct 3, 2026
b76af8f
Rename reblend to shape in the local engine docs
its-janghoon Oct 8, 2026
94c46c2
Merge pull request #66 from redrob-labs/feature/rename-reblend-to-shape
its-janghoon Oct 8, 2026
186afdd
fix: never pick an image model as the default (#72)
its-janghoon Oct 12, 2026
9ffb06d
chore: adopt the shared pull request policy (#83)
its-janghoon Oct 12, 2026
98296d9
Mask PII in the engine (off by default), privacy switch, Console skil…
its-janghoon Oct 12, 2026
4695d20
Image and speech tools, and /redrob/speech and /redrob/transcribe rou…
its-janghoon Oct 12, 2026
a491ba7
Prove the masking switch end to end in the runner test (#86)
its-janghoon Oct 12, 2026
b6c00d8
Read the Redrob Launcher shared account as the last console-key fallb…
its-janghoon Oct 13, 2026
0f49cb1
Publish release assets under the names Redrob Launcher installs (#90)
its-janghoon Oct 13, 2026
e1f4f43
One shared daemon per user, and masking on the OpenAI route (#89)
its-janghoon Oct 13, 2026
bd38f6f
Build, sign and tag a release from one commit (#91)
its-janghoon Oct 13, 2026
fdc5030
Publish the SDK through npm trusted publishing when no token is set (…
its-janghoon Oct 13, 2026
47b1d6e
Hold an attachment while `redrob acp --attach` runs (#93)
its-janghoon Oct 13, 2026
dad942e
Ship the Redrob Launcher release manifest with every release (#94)
its-janghoon Oct 13, 2026
0772133
Read app-managed config for a directory from the state dir (#95)
its-janghoon Oct 13, 2026
efa6e39
Declare the SDK's repository so npm provenance verifies (#97)
its-janghoon Oct 14, 2026
ef3e511
Move the engine-client crate into the engine and label completions wi…
its-janghoon Oct 14, 2026
d482be6
Count authorized requests as activity for the daemon's idle exit (#100)
its-janghoon Oct 14, 2026
cef5cf4
ci: keep the CDN version marker at the newest release (#102)
its-janghoon Oct 14, 2026
358ffb5
ci(cdn-version-marker): do not read the marker back from the bucket (…
its-janghoon Oct 14, 2026
bcb8af0
Sign Windows builds with Azure Artifact Signing over OIDC (#104)
its-janghoon Oct 14, 2026
8b82b5d
Use the shared gitflow, Windows signing and release publish (#105)
its-janghoon Oct 15, 2026
e03b130
Provide the LLM client to the chat-completions route (#107)
its-janghoon Oct 15, 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
61 changes: 0 additions & 61 deletions .github/workflows/branch-sync.yml

This file was deleted.

89 changes: 89 additions & 0 deletions .github/workflows/cdn-version-marker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: CDN version marker

# Keeps `https://cdn.redrob.ai/code/latest/version` equal to the newest GitHub release.
#
# Builds live on GitHub Releases, not on the CDN, since 0.1.0. The marker still matters:
# every `redrob` from the 0.0.x line asks it what the newest version is before running the
# Console installer, and while it said `0.0.12` those installs believed they were current
# and `redrob upgrade` did nothing. Writing the marker on each release is what lets them
# move; the installer they then run downloads from GitHub.
#
# The bucket name and the keys are the organisation's CDN variables, the same ones
# redrob-shape and redrob-browser-agent publish with.

on:
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: Version to write (no leading v). Empty = the newest non-prerelease GitHub release.
required: false
default: ""

permissions:
contents: read

concurrency:
group: cdn-version-marker
cancel-in-progress: false

jobs:
write:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
REDROB_CDN_BUCKET: ${{ vars.REDROB_CDN_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.REDROB_CDN_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.REDROB_CDN_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: us-east-1
REQUESTED: ${{ inputs.version }}
steps:
- name: resolve the version
id: version
run: |
set -euo pipefail
if [ -n "$REQUESTED" ]; then
version="${REQUESTED#v}"
elif [ "${{ github.event_name }}" = "release" ] && [ "${{ github.event.release.prerelease }}" != "true" ]; then
version="${{ github.event.release.tag_name }}"
version="${version#v}"
else
version="$(gh release list --repo "$GITHUB_REPOSITORY" --exclude-pre-releases --exclude-drafts \
--json tagName -q '[.[] | .tagName | select(test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))] | .[0]')"
version="${version#v}"
fi
case "$version" in
[0-9]*.[0-9]*.[0-9]*) ;;
*) echo "::error::not a release version: '$version'"; exit 1 ;;
esac
# The marker never goes backwards: a re-published old release must not demote it.
current="$(curl --fail --silent --location https://cdn.redrob.ai/code/latest/version || true)"
if [ -n "$current" ] && [ "$(printf '%s\n%s\n' "$current" "$version" | sort -V | tail -n1)" != "$version" ]; then
echo "::notice::marker is $current, newer than $version; leaving it."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "version=$version" >> "$GITHUB_OUTPUT"

- name: write code/latest/version
if: ${{ steps.version.outputs.skip != 'true' }}
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
if [ -z "$REDROB_CDN_BUCKET" ]; then
echo "::error::REDROB_CDN_BUCKET is not set"; exit 1
fi
printf '%s\n' "$VERSION" > version
aws s3 cp version "s3://${REDROB_CDN_BUCKET}/code/latest/version" \
--content-type "text/plain" --cache-control "public, max-age=300" --no-progress
# The CDN key can write the bucket but not read it (HeadObject is 403), so the upload's
# own exit status is the proof. The CDN in front has no cache-control on the old object,
# so it may serve the previous marker for up to its default TTL (a day); that is reported,
# not failed.
echo "OK s3://${REDROB_CDN_BUCKET}/code/latest/version = $VERSION"
cdn="$(curl --fail --silent --location https://cdn.redrob.ai/code/latest/version || true)"
if [ "$cdn" != "$VERSION" ]; then
echo "::notice::cdn.redrob.ai still serves '$cdn'; it refreshes when the cached copy expires."
fi
40 changes: 40 additions & 0 deletions .github/workflows/engine-client.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# The Rust client for the shared daemon (crates/engine-client). Runs only when the crate or
# this file changes: the rest of the repository is TypeScript and does not need a Rust toolchain.
name: engine-client

on:
pull_request:
paths:
- "crates/engine-client/**"
- ".github/workflows/engine-client.yml"
push:
branches: [develop, main]
paths:
- "crates/engine-client/**"
- ".github/workflows/engine-client.yml"

permissions:
contents: read

jobs:
check:
name: engine-client (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
defaults:
run:
working-directory: crates/engine-client
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
workspaces: crates/engine-client
- run: cargo fmt --all -- --check
- run: cargo clippy --all-targets --locked -- -D warnings
- run: cargo test --locked
128 changes: 11 additions & 117 deletions .github/workflows/gitflow.yml
Original file line number Diff line number Diff line change
@@ -1,127 +1,21 @@
name: gitflow
# Gitflow rules shared by every redrob-labs repository: branch names are <type>/<slug> from the
# org-wide type list, and main is contained in develop (a back-merge pull request is opened when
# it is not). The rules live in redrob-labs/.github/.github/workflows/gitflow.yml; this file only
# calls them. Do not add a type list here -- there is one, and it is there.

# The back-merge half of the shared gitflow workflow is NOT here: `branch-sync.yml` owns that rule.
#
# One rule this repository already had in prose, now checked.
#
# A rule nothing checks is a preference. It was written down and broken anyway: branches appeared
# under a `kiro/` prefix that no document defines.
#
# A branch name can only be judged when a pull request exists, which is why this runs on
# `pull_request` and not on push.
name: gitflow

on:
pull_request:
# No branch filter. A filter that omits the default branch silently stops running, which is exactly
# what happened in a sibling repository when the default moved to `develop` and three workflows
# kept filtering on `[main, master]`.
types: [opened, reopened, edited, synchronize]
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read
contents: write
pull-requests: write

jobs:
tag-hygiene:
name: no workflow pushes tags in bulk
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 1

- name: Refuse bulk tag pushes and unnamespaced upstream fetches
run: |
set -euo pipefail

# This fork inherited over a thousand upstream tags, which buried its own releases and
# made a `v*` glob deletion unusable. They were deleted. Two command shapes would undo
# that in one run, so neither may appear in a workflow:
#
# a tag push with no tag named republishes whatever is in refs/tags
# a tag fetch from `upstream` refills refs/tags from upstream
#
# The release workflow pushes exactly one tag, named in full as `refs/tags/$TAG`, and
# that is the only allowed shape. Fetching OUR OWN tags from `origin` is fine and is not
# matched: the release-tagging actions depend on it. See docs/VERSIONING.md.
#
# Comment lines are stripped before matching. Without that this gate fails on the very
# comment above, which names both banned shapes in order to explain them.
bad=0
while IFS= read -r file; do
body="$(sed 's/[[:space:]]*#.*$//' "$file")"

if printf '%s\n' "$body" | grep -nE 'git[[:space:]]+push[^|&;]*--(tags|follow-tags)'; then
echo "::error file=$file::pushes tags in bulk; push one tag by its full refs/tags/<name>"
bad=1
fi

# Only upstream's tags are the problem, and only when no refspec redirects them out
# of refs/tags.
if printf '%s\n' "$body" \
| grep -E 'git[[:space:]]+fetch[^|&;]*--tags[^|&;]*upstream|git[[:space:]]+fetch[^|&;]*upstream[^|&;]*--tags' \
| grep -qv 'refs/upstream-tags/'; then
echo "::error file=$file::fetches upstream tags into refs/tags; use '+refs/tags/*:refs/upstream-tags/*'"
bad=1
fi
done < <(find .github/workflows -type f \( -name '*.yml' -o -name '*.yaml' \))

if [ "$bad" -ne 0 ]; then
exit 1
fi
echo "no bulk tag pushes and no unnamespaced upstream tag fetches"

branch-name:
name: branch name follows the convention
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Check the head branch name
env:
HEAD_REF: ${{ github.head_ref }}
BASE_REF: ${{ github.base_ref }}
run: |
set -euo pipefail

# ALLOWED_TYPES is this repository's own list. Keep it in step with the branch table in the
# contributing or branching document; the document is the explanation, this is the gate.
ALLOWED_TYPES='feature sync hotfix release'

# `develop` and `main` reach here through a promotion or a back-merge pull request, which are
# branches in their own right and are not named after a type.
case "$HEAD_REF" in
develop|main) echo "$HEAD_REF is a long-lived branch, not a working branch"; exit 0 ;;
esac

type="${HEAD_REF%%/*}"
slug="${HEAD_REF#*/}"

if [ "$type" = "$HEAD_REF" ]; then
echo "::error::branch '$HEAD_REF' has no type prefix. Use <type>/<slug>, type one of: $ALLOWED_TYPES"
exit 1
fi

ok=false
for t in $ALLOWED_TYPES; do
[ "$type" = "$t" ] && ok=true && break
done

if [ "$ok" != true ]; then
{
echo "### Branch name is not one of this repository's types"
echo
echo "\`$HEAD_REF\` uses the prefix \`$type\`. Allowed: $ALLOWED_TYPES"
echo
echo "A branch name says what the change is, not who or what made it. An agent or tool name"
echo "is not a type: rename the branch and reopen the pull request."
} >> "${GITHUB_STEP_SUMMARY:-/dev/null}"
echo "::error::branch prefix '$type' is not allowed. Use one of: $ALLOWED_TYPES"
exit 1
fi

if [ -z "$slug" ] || [ "$slug" = "$type" ]; then
echo "::error::branch '$HEAD_REF' has an empty slug. Use <type>/<short-slug>."
exit 1
fi

echo "$HEAD_REF is a valid $type branch targeting $BASE_REF"
gitflow:
uses: redrob-labs/.github/.github/workflows/gitflow.yml@main
17 changes: 17 additions & 0 deletions .github/workflows/pr-policy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Pull request policy: at most 3 open pull requests per author, and a maintainer review may be
# requested only after the author has reviewed their own pull request. Rules and defaults live in
# redrob-labs/.github/.github/workflows/pr-policy.yml; pass `with:` to change them for this
# repository.

name: PR policy

on:
pull_request_target:
types: [opened, reopened, review_requested]

permissions:
pull-requests: write

jobs:
policy:
uses: redrob-labs/.github/.github/workflows/pr-policy.yml@main
15 changes: 13 additions & 2 deletions .github/workflows/publish-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,13 @@ concurrency:
group: publish-sdk
cancel-in-progress: false

# `id-token: write` is what npm trusted publishing needs: npm exchanges the GitHub OIDC token for a
# short-lived publish credential, so no NPM_TOKEN secret is stored anywhere. The trust is configured
# on npmjs.com (package settings > Trusted publisher: this repository, this workflow file). When
# NPM_TOKEN IS set it still wins, so the old path keeps working during the switch.
permissions:
contents: read
id-token: write

jobs:
publish:
Expand All @@ -62,6 +67,12 @@ jobs:

- uses: ./.github/actions/setup-bun

# Trusted publishing needs npm >= 11.5.1 (Node 24 ships one); the runner's default is older.
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 24
- run: npm install -g npm@latest && npm --version

- id: resolve
env:
REQUESTED: ${{ inputs.version }}
Expand Down Expand Up @@ -100,8 +111,8 @@ jobs:
run: |
set -euo pipefail
if [ -z "${NPM_TOKEN:-}" ]; then
echo "::error::NPM_TOKEN is not set for this repository or its organization"
exit 1
echo "no NPM_TOKEN: publishing through npm trusted publishing (OIDC)"
exit 0
fi
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
if ! who="$(npm whoami 2>&1)"; then
Expand Down
Loading
Loading