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
59 changes: 59 additions & 0 deletions .github/workflows/dex-release-upgrade.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Prepare Dex release upgrade

on:
repository_dispatch:
types: [dex-release-published]

permissions:
contents: read

concurrency:
group: superagent-dex-${{ github.event.client_payload.version }}
cancel-in-progress: false

jobs:
upgrade:
if: github.event.sender.type == 'Bot'
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Create repository-scoped release automation token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.RELEASE_AUTOMATION_APP_ID }}
private-key: ${{ secrets.RELEASE_AUTOMATION_PRIVATE_KEY }}
owner: superdurable
repositories: superagent
- uses: actions/checkout@v7
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Update immutable Dex release pins
env:
DEX_MANIFEST_SHA256: ${{ github.event.client_payload.manifest_sha256 }}
DEX_MANIFEST_URL: ${{ github.event.client_payload.manifest_url }}
run: |
python3 script/update_dex_release.py \
--manifest-url "${DEX_MANIFEST_URL}" \
--manifest-sha256 "${DEX_MANIFEST_SHA256}"
GOWORK=off go mod tidy
python3 -m unittest script/update_dex_release_test.py
python3 script/check_dex_release.py
- name: Open draft upgrade pull request before compilation
uses: peter-evans/create-pull-request@v8
with:
token: ${{ steps.app-token.outputs.token }}
branch: automation/dex-v${{ github.event.client_payload.version }}
delete-branch: true
draft: true
commit-message: Upgrade SuperAgent to Dex ${{ github.event.client_payload.version }}
title: Upgrade SuperAgent to Dex ${{ github.event.client_payload.version }}
body: |
Automated from the immutable Dex compatibility manifest.

Review `openFlowsCompatibility` before merging. It defaults to
`cancel-required`; automation never claims open-Flow compatibility.

Normal pull-request CI owns compilation and tests. When a Dex SDK
API breaks, continue the required migration in this draft PR.
68 changes: 68 additions & 0 deletions .github/workflows/github-release-ui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,71 @@ jobs:

install_url="https://github.com/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/${asset_name}"
echo "Install with: npm install ${install_url} react" >> "${GITHUB_STEP_SUMMARY}"

notify-downstreams:
name: Request IaC and SuperVerse upgrades
if: github.event_name == 'release'
needs: attach
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.event.release.tag_name }}
- name: Validate release compatibility declaration
id: release
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
python3 script/check_dex_release.py
python3 - "${RELEASE_TAG}" "${GITHUB_OUTPUT}" <<'PY'
import json
from pathlib import Path
import re
import subprocess
import sys

tag, output_path = sys.argv[1:]
if re.fullmatch(r"v[0-9]+\.[0-9]+\.[0-9]+", tag) is None:
raise SystemExit("SuperAgent release tag is not stable semver")
lock = json.loads(Path("dex-release.lock.json").read_text(encoding="utf-8"))
commit = subprocess.run(
["git", "rev-parse", "HEAD"], check=True, capture_output=True, text=True
).stdout.strip()
with Path(output_path).open("a", encoding="utf-8") as output:
for name, value in {
"dex_version": lock["release"],
"manifest_url": lock["manifest"]["url"],
"manifest_sha256": lock["manifest"]["sha256"],
"open_flows_compatibility": lock["openFlowsCompatibility"],
"superagent_commit": commit,
}.items():
print(f"{name}={value}", file=output)
PY
- name: Create repository-scoped release automation token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.RELEASE_AUTOMATION_APP_ID }}
private-key: ${{ secrets.RELEASE_AUTOMATION_PRIVATE_KEY }}
owner: superdurable
repositories: iac,superverse
- name: Dispatch audited downstream upgrades
env:
DEX_MANIFEST_SHA256: ${{ steps.release.outputs.manifest_sha256 }}
DEX_MANIFEST_URL: ${{ steps.release.outputs.manifest_url }}
DEX_VERSION: ${{ steps.release.outputs.dex_version }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
OPEN_FLOWS_COMPATIBILITY: ${{ steps.release.outputs.open_flows_compatibility }}
SUPERAGENT_COMMIT: ${{ steps.release.outputs.superagent_commit }}
SUPERAGENT_RELEASE: ${{ github.event.release.tag_name }}
run: |
for repository in iac superverse; do
gh api --method POST "repos/superdurable/${repository}/dispatches" \
-f event_type=superagent-release-published \
-f "client_payload[dex_version]=${DEX_VERSION}" \
-f "client_payload[manifest_url]=${DEX_MANIFEST_URL}" \
-f "client_payload[manifest_sha256]=${DEX_MANIFEST_SHA256}" \
-f "client_payload[superagent_release]=${SUPERAGENT_RELEASE}" \
-f "client_payload[superagent_commit]=${SUPERAGENT_COMMIT}" \
-f "client_payload[open_flows_compatibility]=${OPEN_FLOWS_COMPATIBILITY}"
done
20 changes: 8 additions & 12 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Snapshot is the only durable current-interaction and reconciliation read model.
Archive paging is an immutable history continuation. Each page uses one
read-only Flow RPC that loads `AgentState` and the retained archive map, then
returns one exact chunk without loading current interaction state or pending
Channels. Dex Go SDK `v0.10.0` fixes selective loads at RPC registration, so an
Channels. Dex Go SDK `v0.9.1` fixes selective loads at RPC registration, so an
input-selected AttributeMap instance cannot be loaded independently.

Commands follow Dex's transactional RPC model. There is no permanent command
Expand Down Expand Up @@ -140,11 +140,11 @@ replacement. They also receive Dex attempt metadata.

The browser performs one generated `GET /products/ai-agent/snapshot` on load and
atomically replaces history, description, queued messages, steered messages,
and Run identity through one reducer action. Before invoking the Snapshot RPC,
the backend checks the indexed Flow lifecycle so a terminated Flow cannot return
its last running projection. It then lists the configured recent tail of each
Stream, applies those events chronologically, and long-polls from the newest
returned resume token. The browser orders every
and Run identity through one reducer action. Snapshot is an application-state
query, not a Dex lifecycle projection. It remains readable from retained closed
Flow history and returns the last durable application view. The browser then
lists the configured recent tail of each Stream, applies those events
chronologically, and long-polls from the newest returned resume token. The browser orders every
observed activity event, reasoning summary, live assistant response, and durable
message in one timeline by creation time. Reasoning entries are keyed by the
producing model invocation source. Completion activity marks later text from
Expand All @@ -159,9 +159,7 @@ pending question increments it only when `AnsweredUserInputs` is empty, then
waits exclusively for an answer. The
browser takes the first Snapshot round as a watermark, then long-polls
for `round > watermark`. Each response returns the actual matched round, which
becomes the next watermark before requesting Snapshot. The ongoing round wait
also discovers Flow closure; Snapshot's lifecycle guard remains the terminal
recovery path.
becomes the next watermark before requesting Snapshot.

Every consumed queued message, steered message, or Plan execution request emits
one `input_consumed` Activity event with exact application IDs or revision and
Expand Down Expand Up @@ -190,8 +188,6 @@ reconcile also close this gate. Ordinary Stream events other than explicit
Snapshot controls do not request Snapshot. A visible-page configurable
single-shot freshness timer defaults to 60 seconds and starts only after the
prior Snapshot finishes, so an intervening read resets the complete delay.
Terminal reconciliation stops Streams, Attribute waits, Snapshot work, and the
timer.

Resume tokens belong to the live subscription and are not durable UI state.
Activity events are independent timeline rows keyed by resume token. A page
Expand Down Expand Up @@ -330,7 +326,7 @@ Runtime metadata therefore remains stable for the logical call.

`internal/app` owns every long-lived resource. Startup validates configuration,
discovers MCP, constructs providers, opens BlobCache, starts the Worker, waits
for its listener, marks readiness, and then serves the API. The Dex Go SDK `v0.10.0`
for its listener, marks readiness, and then serves the API. The Dex Go SDK `v0.9.1`
Worker negotiates a compatible Server protocol before synchronizing indexes or
binding. Any startup failure closes everything already constructed.

Expand Down
22 changes: 15 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,25 @@ against the installed released SDK and a version-matched runnable example or
real-server compile-contract test.

Snapshot, Stream, Channel size snapshot, and Attribute wait code target Dex Go
SDK `v0.10.0`. Workers negotiate the Server protocol before binding. Recheck
SDK `v0.9.1` and Server `v0.10.0`. Workers negotiate the Server protocol before
binding, so deployments upgrade the Server before the Worker. Recheck
the installed SDK source and the installed skill before changing resource
projection or errors. Never infer an API from a design screenshot or unreleased
branch.

Dex Go SDK and dexcli are independent direct dependencies. Run
`script/update_dex_versions.py` with explicit component versions, then run
`script/check_dex_versions.py`. The updater reads dexcli's native
`checksums.txt`; SuperAgent does not consume a cross-component compatibility
manifest. Resolve SDK API changes in the same pull request. Normal compilation,
real-Server integration, and browser E2E are the merge gates.
`dex-release.lock.json` binds the Server to its immutable compatibility manifest.
It binds an independently released Go SDK patch to its tag, source commit, and
Go module checksums. For a Server-only upgrade, run
`script/update_dex_release.py` with `--server-only`, `--manifest-url`, and
`--manifest-sha256`. This preserves either SDK lock form and requires the SDK
and Server protocol intervals to overlap. Dex Go SDK `v0.9.1` registers RPCs
explicitly and fixes their execution options at Flow registration.
A Dex publication opens an automated upgrade PR with open Flow
compatibility set to `cancel-required` for review. Publishing the subsequent
SuperAgent release dispatches the reviewed IaC and SuperVerse upgrades.
The automation opens the draft after asset validation and mechanical pin
updates, before product compilation. Resolve breaking SDK API migrations in
that draft; normal pull-request CI remains the merge gate.

## Deployment boundary

Expand Down
11 changes: 4 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: audit-web build-api build-web check check-agent-rules check-dex-versions check-flow-definition \
.PHONY: audit-web build-api build-web check check-agent-rules check-flow-definition \
check-generated copyright-check flow-visualize format-check fuzz generate \
generate-go generate-web governance-check install-dexcli install-osv-scanner install-temporal lint lint-go lint-web lint-workflows \
test test-agent test-api test-app test-config test-dex-integration test-mcp test-model test-openai-live \
Expand Down Expand Up @@ -48,9 +48,6 @@ copyright-check:

governance-check: check-agent-rules copyright-check

check-dex-versions:
@python3 script/check_dex_versions.py

install-dexcli: $(DEXCLI_BINARY)

$(DEXCLI_BINARY): script/install-dexcli.sh
Expand Down Expand Up @@ -83,8 +80,8 @@ check-flow-definition: install-dexcli
sed -n '/"diagnostics"/,$$p' "$${flow_definition}" >&2; \
exit 1; \
fi; \
if grep -Fq '"name": "ExecuteTool"' "$${flow_definition}"; then \
echo "Flow definition must not contain the removed ExecuteTool Step" >&2; \
if ! grep -Fq '"name": "ExecuteTool"' "$${flow_definition}"; then \
echo "Flow definition must contain the canonical ExecuteTool Step" >&2; \
exit 1; \
fi; \
for channel in answeredUserInputsChannel queuedUserMessagesChannel steeredUserMessagesChannel toolApprovalsChannel toolRecoveryDecisionsChannel parallelToolResultsChannel planExecutionsChannel; do \
Expand Down Expand Up @@ -171,4 +168,4 @@ test-web:
test-openai-live:
@GOCACHE=$(GO_BUILD_CACHE) GOWORK=off go test -tags=live -count=1 -run '^TestLiveOpenAIResponses$$' ./internal/model

check: governance-check check-dex-versions check-generated format-check build-api build-web vet lint test test-race test-web vulnerability-check audit-web
check: governance-check check-generated format-check build-api build-web vet lint test test-race test-web vulnerability-check audit-web
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ and resource model.

- Go matching [`go.mod`](go.mod)
- Node.js and npm compatible with [`web/package-lock.json`](web/package-lock.json)
- A Dex Server compatible with Dex Go SDK `v0.10.0`
- A Dex Server `v0.10.0`
- A writable directory for disposable Dex BlobCache data

## Quick start
Expand All @@ -102,7 +102,9 @@ make build-api
make build-web
```

Start a compatible Dex server, then run the API and Worker:
Start a compatible Dex server. Dex Go SDK `v0.9.1` Workers require the Server
compatibility RPC, so upgrade the Server before the Worker. Then run the API and
Worker:

```bash
SUPERAGENT_HTTP_ALLOWED_ORIGINS=http://127.0.0.1:3000 ./bin/superagent
Expand Down Expand Up @@ -150,11 +152,11 @@ persisted in Dex state or logged. Copy
[`web/mcp-servers.example.yaml`](web/mcp-servers.example.yaml) to configure
trusted MCP servers.

Each configured tool defaults to `running_type: short_running` and a 60-second
heartbeat timeout. Use `long_running` when more than half of expected calls
exceed five seconds. This is a Dex placement optimization, not a timeout or
SLA; short-running calls may fall back and complete normally. Keep the
heartbeat default unless a healthy tool can remain silent longer.
Each configured tool defaults to `running_type: short_running`. Use
`long_running` when more than half of expected calls exceed five seconds. This
is a Dex placement optimization, not a timeout or SLA; short-running calls may
fall back and complete normally. Every regular Step attempt uses Dex's
one-minute heartbeat timeout.

For a cross-origin frontend deployment, add its exact origin to
`SUPERAGENT_HTTP_ALLOWED_ORIGINS`. Wildcards and credentialed cross-origin
Expand Down
18 changes: 0 additions & 18 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,6 @@ const (
AgentStatusApplyingSteering = agentinternal.AgentStatusApplyingSteering
)

const (
FlowStatusRunning = agentinternal.FlowStatusRunning
FlowStatusCompleted = agentinternal.FlowStatusCompleted
FlowStatusFailed = agentinternal.FlowStatusFailed
FlowStatusTerminated = agentinternal.FlowStatusTerminated
FlowStatusCanceled = agentinternal.FlowStatusCanceled
FlowStatusContinuedAsNew = agentinternal.FlowStatusContinuedAsNew

FlowErrorTypeStepDecision = agentinternal.FlowErrorTypeStepDecision
FlowErrorTypeClientAPI = agentinternal.FlowErrorTypeClientAPI
FlowErrorTypeWorkerMethod = agentinternal.FlowErrorTypeWorkerMethod
FlowErrorTypeInvalidUserCode = agentinternal.FlowErrorTypeInvalidUserCode
FlowErrorTypeInternal = agentinternal.FlowErrorTypeInternal
FlowErrorTypeTimeout = agentinternal.FlowErrorTypeTimeout
)

const (
InteractionModeChat = agentinternal.InteractionModeChat
InteractionModePlanning = agentinternal.InteractionModePlanning
Expand Down Expand Up @@ -188,8 +172,6 @@ type (

AgentStatus = agentinternal.AgentStatus
WaitingInputRound = agentinternal.WaitingInputRound
FlowStatus = agentinternal.FlowStatus
FlowErrorType = agentinternal.FlowErrorType
InteractionMode = agentinternal.InteractionMode
PlanStatus = agentinternal.PlanStatus
TaskStatus = agentinternal.TaskStatus
Expand Down
34 changes: 1 addition & 33 deletions api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -886,31 +886,17 @@ components:
additionalProperties: false
required:
- runId
- flowStatus
- errorType
- errorMessage
- history
- description
- queued
- steered
properties:
runId:
$ref: "#/components/schemas/RunId"
flowStatus:
$ref: "#/components/schemas/FlowStatus"
errorType:
allOf:
- $ref: "#/components/schemas/FlowErrorType"
nullable: true
errorMessage:
type: string
nullable: true
history:
$ref: "#/components/schemas/HistoryPage"
description:
allOf:
- $ref: "#/components/schemas/AgentDescription"
nullable: true
$ref: "#/components/schemas/AgentDescription"
queued:
type: array
items:
Expand All @@ -935,24 +921,6 @@ components:
allOf:
- $ref: "#/components/schemas/Sequence"
nullable: true
FlowStatus:
type: string
enum:
- running
- completed
- failed
- terminated
- canceled
- continued_as_new
FlowErrorType:
type: string
enum:
- step_decision
- client_api
- worker_method
- invalid_user_code
- internal
- timeout
SequencedMessage:
type: object
additionalProperties: false
Expand Down
Loading