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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ jobs:
run: |
set -euo pipefail
dex_log="$RUNNER_TEMP/dexcli.log"
PATH="$PWD/.cache/temporal-v1.8.2:$PATH" .cache/dexcli-v0.9.0 dev \
PATH="$PWD/.cache/temporal-v1.8.2:$PATH" .cache/dexcli-v0.10.0 dev \
-open=false \
-blob-store-dir "$RUNNER_TEMP/dex-blobs" \
-sqlite-db-filename "$RUNNER_TEMP/dex.sqlite.db" \
Expand Down
59 changes: 0 additions & 59 deletions .github/workflows/dex-release-upgrade.yml

This file was deleted.

68 changes: 0 additions & 68 deletions .github/workflows/github-release-ui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,71 +106,3 @@ 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
8 changes: 5 additions & 3 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,10 @@ prompts, and cumulative context summaries are separate typed Attributes.

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 one exact archive chunk, without
loading current interaction state or pending Channels.
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
input-selected AttributeMap instance cannot be loaded independently.

Commands follow Dex's transactional RPC model. There is no permanent command
receipt, caller request ID, payload fingerprint, global mutation revision, or
Expand Down Expand Up @@ -328,7 +330,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 `v0.9.0`
for its listener, marks readiness, and then serves the API. The Dex Go SDK `v0.10.0`
Worker negotiates a compatible Server protocol before synchronizing indexes or
binding. Any startup failure closes everything already constructed.

Expand Down
16 changes: 7 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,17 @@ 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 and Server `v0.9.0`. Version `v0.9.0` Workers negotiate the Server protocol
before binding, so deployments upgrade the Server before the Worker. Recheck
SDK `v0.10.0`. Workers negotiate the Server protocol before binding. 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-release.lock.json` binds the direct Go SDK requirement to one immutable
Dex manifest. 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.
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.

## Deployment boundary

Expand Down
13 changes: 10 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
.PHONY: audit-web build-api build-web check check-agent-rules check-flow-definition \
.PHONY: audit-web build-api build-web check check-agent-rules check-dex-versions 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 \
test-full-stack-e2e test-integration test-public-api test-race test-server-integration test-web vet vulnerability-check

GO_BUILD_CACHE := $(CURDIR)/.cache/go-build
GO_PACKAGES := ./agent/... ./cmd/... ./internal/... ./model/... ./toolcontract/...
DEXCLI_VERSION := v0.9.0
DEXCLI_VERSION := v0.10.0
DEXCLI_BINARY := $(CURDIR)/.cache/dexcli-$(DEXCLI_VERSION)
OSV_SCANNER_VERSION := v2.5.1
OSV_SCANNER_BINARY := $(CURDIR)/.cache/osv-scanner-$(OSV_SCANNER_VERSION)
Expand Down Expand Up @@ -48,6 +48,9 @@ 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 @@ -80,6 +83,10 @@ 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; \
exit 1; \
fi; \
for channel in answeredUserInputsChannel queuedUserMessagesChannel steeredUserMessagesChannel toolApprovalsChannel toolRecoveryDecisionsChannel parallelToolResultsChannel planExecutionsChannel; do \
if ! grep -Fq "\"id\": \"resource:channel:$${channel}\"" "$${flow_definition}" || \
! grep -Fq "\"resourceId\": \"resource:channel:$${channel}\"" "$${flow_definition}"; then \
Expand Down Expand Up @@ -164,4 +171,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-generated format-check build-api build-web vet lint test test-race test-web vulnerability-check audit-web
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
12 changes: 8 additions & 4 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 `v0.9.0` server
- A Dex Server compatible with Dex Go SDK `v0.10.0`
- A writable directory for disposable Dex BlobCache data

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

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

```bash
SUPERAGENT_HTTP_ALLOWED_ORIGINS=http://127.0.0.1:3000 ./bin/superagent
Expand Down Expand Up @@ -152,6 +150,12 @@ 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.

For a cross-origin frontend deployment, add its exact origin to
`SUPERAGENT_HTTP_ALLOWED_ORIGINS`. Wildcards and credentialed cross-origin
requests are intentionally unsupported. Serve `config.json` with
Expand Down
4 changes: 4 additions & 0 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ const (
ToolOutcomeKnownFailure = agentinternal.ToolOutcomeKnownFailure
ToolOutcomeUnknown = agentinternal.ToolOutcomeUnknown

ToolRunningTypeShortRunning = agentinternal.ToolRunningTypeShortRunning
ToolRunningTypeLongRunning = agentinternal.ToolRunningTypeLongRunning

ToolRetryExhaustionPolicyManualRecovery = agentinternal.ToolRetryExhaustionPolicyManualRecovery
ToolRetryExhaustionPolicyContinueWithUnknown = agentinternal.ToolRetryExhaustionPolicyContinueWithUnknown

Expand Down Expand Up @@ -194,6 +197,7 @@ type (
EventKind = agentinternal.EventKind
Provider = agentinternal.Provider
ToolOutcome = agentinternal.ToolOutcome
ToolRunningType = agentinternal.ToolRunningType
ToolRetryExhaustionPolicy = agentinternal.ToolRetryExhaustionPolicy
ToolRecoveryResolution = agentinternal.ToolRecoveryResolution
ToolRecoveryAction = agentinternal.ToolRecoveryAction
Expand Down
17 changes: 0 additions & 17 deletions dex-release.lock.json

This file was deleted.

5 changes: 5 additions & 0 deletions docs/adr/0011-dex-owned-tool-retries.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ Go error and use Dex retry. Exhaustion follows the tool definition's recovery
policy. It defaults to the manual boundary introduced by ADR 0013; explicitly
configured tools may route to `RecoverToolExecution` and continue with unknown.

New Agent Flows default Step durability to ASYNC. Short-running tools inherit
that default and may fall back to regular execution. Long-running tools override
Execute durability to SYNC. Tool policy also supplies heartbeat timeout, while
attempt timeout bounds both Dex execution and the registry child context.

Approval and CallID remain stable across attempts. External effects promise
recoverable at-least-once execution, not exactly-once execution.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ Consumed IDs suppress stale queue data until durable history replaces the
temporary projection. Snapshot remains the only authoritative durable
reconciliation model.

Deployments must use Dex Server and Go SDK `v0.9.0`, and the matching Worker and
browser behavior together. The Server must be upgraded first because `v0.9.0`
Workers reject Servers without protocol negotiation. Deployments must stop or
clear Agent Flows created with the removed schema before rollout; there is no
old-Attribute or Runtime Lease compatibility shim.
Deployments must update the Worker and browser behavior together. The product
has not launched, so removed Agent schemas have no migration path. Runtime
protocol negotiation still rejects an unsupported Server.
41 changes: 41 additions & 0 deletions docs/adr/0014-registered-rpc-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# ADR 0014: Register immutable RPC execution options

## Status

Accepted on 2026-09-17.

## Context

Dex Go SDK `v0.10.0` replaces reflected RPC discovery with explicit `GetRPCs`
definitions. Timeout, locks, transactional execution, and selective collection
loads belong to the registered RPC definition. Callers can impose a shorter
context deadline, but cannot change those options per invocation.

Most Agent RPCs always use the same resources. `GetArchivedMessages` is the
exception: its input selects one `ArchivedMessages` instance. The released SDK
cannot derive an AttributeMap instance load from RPC input.

## Decision

`AIAgentFlow.GetRPCs` explicitly registers every production RPC and its complete
execution policy. Client calls provide only the Flow ID, registered method,
typed input, and output destination. Snapshot uses a five-second registered
timeout. Commands and archive reads use twenty seconds. Existing Attribute
locks and transactional Channel mutations remain attached to their RPCs.

`GetArchivedMessages` registers a whole-map `ArchivedMessages` load and returns
only the requested immutable ten-message chunk. It still excludes current
messages and pending Channels. Retention remains the bound on loaded archive
state. Integration-only RPCs use the same explicit registration contract.

## Consequences

Worker and Client registries share one visible RPC contract, and invalid loads
or locks fail during registry construction. Call sites cannot accidentally
weaken transactional behavior or select undeclared state.

An archive page now hydrates every retained archive chunk before returning one
page. This is a known cost of the released `v0.10.0` contract, not an SLA change.
A future bounded design requires a new durable storage boundary or a released
SDK facility for input-derived instance selection; it must not emulate mutable
per-call options in application code.
Loading