diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 17b4480cf9..f438212aba 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -2,9 +2,10 @@ set -euo pipefail staged_go_files=$(git diff --cached --name-only --diff-filter=ACM -- '*.go' || true) -staged_web_src=$(git diff --cached --name-only --diff-filter=ACM -- 'cmd/gc/dashboard/web/src/' 'cmd/gc/dashboard/web/index.html' 'cmd/gc/dashboard/web/public/' 'cmd/gc/dashboard/web/package.json' 'cmd/gc/dashboard/web/vite.config.ts' 'cmd/gc/dashboard/web/tsconfig.json' || true) +staged_web_src=$(git diff --cached --name-only --diff-filter=ACM -- 'cmd/gc/dashboard/web/src/' 'cmd/gc/dashboard/web/index.html' 'cmd/gc/dashboard/web/public/' 'cmd/gc/dashboard/web/package.json' 'cmd/gc/dashboard/web/openapi-ts.config.ts' 'cmd/gc/dashboard/web/vite.config.ts' 'cmd/gc/dashboard/web/tsconfig.json' || true) +staged_docs=$(git diff --cached --name-only --diff-filter=ACM -- '*.md' 'docs/**' 'engdocs/**' 'plans/**' 'specs/**' 'AGENTS.md' 'CONTRIBUTING.md' 'README.md' 'TESTING.md' || true) -if [ -z "$staged_go_files" ] && [ -z "$staged_web_src" ]; then +if [ -z "$staged_go_files" ] && [ -z "$staged_web_src" ] && [ -z "$staged_docs" ]; then exit 0 fi @@ -22,11 +23,22 @@ if [ -n "$staged_go_files" ]; then go run ./cmd/genspec git add internal/api/openapi.json docs/schema/openapi.json docs/schema/openapi.txt + go generate ./internal/api/genclient + git add internal/api/genclient/client_gen.go + # Regenerate the config/schema docs derived from Go structs and # stage every generated artifact so commits do not leave the # worktree dirty. go run ./cmd/genschema git add docs/schema/city-schema.json docs/schema/city-schema.txt docs/reference/config.md docs/reference/cli.md + + make lint + make vet + make test +fi + +if [ -n "$staged_docs" ]; then + make check-docs fi # Dashboard SPA rebuild: whenever the spec changes OR the SPA source @@ -36,20 +48,12 @@ fi if command -v npm >/dev/null 2>&1; then spec_changed=$(git diff --cached --name-only --diff-filter=ACM -- 'internal/api/openapi.json' || true) if [ -n "$spec_changed" ] || [ -n "$staged_web_src" ]; then - ( - cd cmd/gc/dashboard/web - if [ ! -d node_modules ]; then - npm install --silent - fi - npm run gen --silent - # Typecheck BEFORE build: vite's build transpiles TS to JS and - # silently ignores type errors. Without this step, SPA type - # regressions against the regenerated spec (e.g. a query param - # tightening from string to boolean) ship unnoticed. `tsc - # --noEmit` fails fast and stops the commit. - npm run typecheck --silent - npm run build --silent - ) + # Typecheck BEFORE build: vite's build transpiles TS to JS and + # silently ignores type errors. The Makefile target also runs the + # Vitest suite, builds dist/, and smoke-runs the compiled SPA via + # Vite preview so a bundle that builds but won't serve is caught + # before CI. + make dashboard-check dashboard-smoke git add cmd/gc/dashboard/web/dist fi else diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8ac61c4f5..f212193348 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,7 @@ jobs: packs: ${{ steps.filter.outputs.packs }} worker: ${{ steps.filter.outputs.worker }} worker_phase2: ${{ steps.filter.outputs.worker_phase2 }} + cmd_gc_process: ${{ steps.filter.outputs.cmd_gc_process }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 @@ -70,6 +71,14 @@ jobs: - 'internal/runtime/**' - 'internal/config/**' - 'cmd/gc/**' + cmd_gc_process: + - 'go.mod' + - 'go.sum' + - '.github/workflows/**' + - 'Makefile' + - 'cmd/gc/**' + - 'internal/**' + - 'examples/gastown/packs/**' # Always runs: lint, fmt, vet, unit tests, docs, acceptance, coverage. check: @@ -148,6 +157,27 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} verbose: true + cmd-gc-process: + name: cmd/gc process suite + needs: changes + if: needs.changes.outputs.cmd_gc_process == 'true' + runs-on: ubuntu-latest + timeout-minutes: 20 + env: + DOLT_VERSION: "1.86.1" + BD_VERSION: "v1.0.0" + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: ./.github/actions/setup-gascity-ubuntu + with: + dolt-version: ${{ env.DOLT_VERSION }} + bd-version: ${{ env.BD_VERSION }} + install-claude-cli: "true" + - name: Install tools + run: make install-tools + - name: Run cmd/gc process suite + run: make test-cmd-gc-process + # Runs always, but remains non-blocking while integration/provider paths are still stabilizing. integration-shards: name: Integration / ${{ matrix.shard_name }} diff --git a/.github/workflows/homebrew-tap-smoke.yml b/.github/workflows/homebrew-tap-smoke.yml index bffbaeba8a..e8b8ee4921 100644 --- a/.github/workflows/homebrew-tap-smoke.yml +++ b/.github/workflows/homebrew-tap-smoke.yml @@ -34,7 +34,7 @@ jobs: run: brew uninstall --force gascity || true - name: Install gascity from the live tap - run: brew install --build-from-source gastownhall/gascity/gascity + run: brew install gastownhall/gascity/gascity - name: Run formula test block run: brew test gastownhall/gascity/gascity diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f072cce5c6..f0c73e93e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,8 @@ on: push: tags: - "v*" + # Manual dispatch is only for rerunning a release from a v* tag. Publishing + # jobs below are tag-gated and skip branch refs. workflow_dispatch: concurrency: @@ -16,6 +18,7 @@ permissions: jobs: release: name: Release + if: ${{ github.repository == 'gastownhall/gascity' && startsWith(github.ref, 'refs/tags/v') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -48,3 +51,136 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GORELEASER_CURRENT_TAG: ${{ github.ref_name }} + + update-homebrew-formula: + name: Update Homebrew formula + if: ${{ github.repository == 'gastownhall/gascity' && startsWith(github.ref, 'refs/tags/v') }} + needs: release + runs-on: ubuntu-latest + env: + HAS_HOMEBREW_APP: ${{ secrets.HOMEBREW_TAP_APP_ID != '' && secrets.HOMEBREW_TAP_APP_PRIVATE_KEY != '' }} + HAS_HOMEBREW_PAT: ${{ secrets.HOMEBREW_TAP_TOKEN != '' }} + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Extract version from tag + id: version + run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" + + - name: Mint Homebrew tap token + id: homebrew-token + if: ${{ env.HAS_HOMEBREW_APP == 'true' }} + uses: actions/create-github-app-token@v3 + with: + app-id: ${{ secrets.HOMEBREW_TAP_APP_ID }} + private-key: ${{ secrets.HOMEBREW_TAP_APP_PRIVATE_KEY }} + owner: gastownhall + repositories: homebrew-gascity + permission-contents: write + + - name: Generate and push Homebrew formula + if: ${{ env.HAS_HOMEBREW_APP == 'true' || env.HAS_HOMEBREW_PAT == 'true' }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HOMEBREW_TAP_TOKEN: ${{ steps.homebrew-token.outputs.token || secrets.HOMEBREW_TAP_TOKEN }} + run: | + version="${{ steps.version.outputs.version }}" + tag="v${version}" + base_url="https://github.com/gastownhall/gascity/releases/download/${tag}" + + gh release download "${tag}" --pattern "gascity_${version}_checksums.txt" --dir /tmp + checksums_file="/tmp/gascity_${version}_checksums.txt" + + get_sha256() { + local sha + sha=$(grep "$1" "$checksums_file" | awk '{print $1}') + if [ -z "$sha" ]; then + echo "ERROR: missing checksum for $1" >&2 + exit 1 + fi + echo "$sha" + } + + darwin_arm64_sha=$(get_sha256 "gascity_${version}_darwin_arm64.tar.gz") + darwin_amd64_sha=$(get_sha256 "gascity_${version}_darwin_amd64.tar.gz") + linux_amd64_sha=$(get_sha256 "gascity_${version}_linux_amd64.tar.gz") + linux_arm64_sha=$(get_sha256 "gascity_${version}_linux_arm64.tar.gz") + + cat > /tmp/gascity.rb < # create a new city + gc start # start an existing city + EOS + end + + test do + assert_match version.to_s, shell_output("#{bin}/gc version") + end + end + FORMULA + sed -i 's/^ //' /tmp/gascity.rb + + cd /tmp + git clone "https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/gastownhall/homebrew-gascity.git" + cp gascity.rb homebrew-gascity/Formula/gascity.rb + cd homebrew-gascity + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Formula/gascity.rb + git commit -m "gascity ${version}" || echo "No changes to commit" + git push + + - name: Skip Homebrew formula update + if: ${{ env.HAS_HOMEBREW_APP != 'true' && env.HAS_HOMEBREW_PAT != 'true' }} + run: echo "No Homebrew tap credential configured; skipping tap update." diff --git a/.goreleaser.yml b/.goreleaser.yml index e34054defc..96b259b454 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -21,14 +21,9 @@ release: prerelease: auto replace_existing_artifacts: true -# Homebrew distribution is handled by a hand-authored, source-built formula -# that lives outside this repo (in `gastownhall/homebrew-gascity` and, once -# merged, `Homebrew/homebrew-core`). Removed the autogenerated binary-install -# `brews:` block because: -# - homebrew-core rejects binary-only formulae; it requires a source build. -# - Keeping both a GoReleaser-stamped tap formula and a hand-authored -# core formula guarantees drift between the two sources of truth. -# See RELEASING.md for the new flow. +# Homebrew tap distribution is generated by .github/workflows/release.yml after +# GoReleaser uploads all release archives. The tap formula installs the release +# assets directly; no source build or Go toolchain is required for users. changelog: sort: asc diff --git a/AGENTS.md b/AGENTS.md index 6e03ebdaf5..dccabf9b92 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,8 +23,8 @@ becomes one configuration among many. has `*_test.go` files next to the code. Integration tests that need real infrastructure (tmux, filesystem) go in `test/` with build tags. -**The spec is a reference, not a blueprint.** When the DX conflicts -with the spec, DX wins. We update the spec to match. +**The architecture docs are a reference, not a blueprint.** When the DX +conflicts with the docs, DX wins. We update the docs to match. ## Architecture @@ -89,9 +89,10 @@ Capabilities activate progressively via config presence. | 7 | Orders | | 8 | Full orchestration | -## Architecture specs +## Architecture docs -Read **`specs/architecture.md`** before touching: +Read **`engdocs/architecture/api-control-plane.md`** and +**`engdocs/contributors/huma-usage.md`** before touching: - `internal/api/` (HTTP + SSE API layer) - `cmd/gc/` (CLI) — especially anything that constructs events, @@ -104,7 +105,7 @@ Read **`specs/architecture.md`** before touching: `cmd/gc/dashboard/web/src/generated/` Load-bearing invariants enforced by CI (violating any fails the -build; full rationale is in the spec): +build; full rationale is in the architecture docs): - **Object model at the center.** `internal/{beads, mail, convoy, formula, agent, events, session, sling, ...}` is the canonical @@ -113,9 +114,9 @@ build; full rationale is in the spec): domain logic. - **Typed wire.** No hand-written JSON on any HTTP or SSE wire path; no `map[string]any` or `json.RawMessage` on wire types - (two documented exceptions live in the spec). All endpoints are - Huma-registered; the OpenAPI spec is generated, never - hand-written (`TestOpenAPISpecInSync`). + (documented exceptions live in the API control-plane doc). All + endpoints are Huma-registered; the OpenAPI spec is generated, + never hand-written (`TestOpenAPISpecInSync`). - **Typed events.** Every constant in `events.KnownEventTypes` must have a registered payload via `events.RegisterPayload(constant, sample)`. Use @@ -131,7 +132,7 @@ These decisions are final. Do not revisit them. `city.toml`, `.gc/` runtime state, and `rigs/` infrastructure. - **Fresh binary, not a Gas Town fork.** We build `gc` from scratch. - **TOML for config.** `pack.toml` (definition) and `city.toml` (deployment) are the config files. -- **Tutorials win over spec.** When the spec disagrees, we update the spec. +- **Tutorials win over architecture docs.** When the docs disagree, we update the docs. - **No premature abstraction.** Don't build interfaces until two implementations exist. - **Mayor is overseer, not worker.** The mayor plans; coding agents work. @@ -218,6 +219,14 @@ Before considering any task complete: - `go test ./...` passes - `go vet ./...` clean +- `.githooks/pre-commit` is active locally (`git config core.hooksPath` + prints `.githooks`) and has run for the staged change +- `make dashboard-check` passes for any change touching `internal/api/`, + `internal/api/openapi.json`, `docs/schema/openapi.*`, + `cmd/gc/dashboard/`, or generated dashboard types +- The dashboard starts locally and serves the app for dashboard/API-schema + changes; use `npm run preview -- --host 127.0.0.1 --port ` from + `cmd/gc/dashboard/web` after `make dashboard-check` - Every exported function has a doc comment - No premature abstractions - Tests cover happy path AND edge cases diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46c8403868..09e0778073 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,10 @@ contributors. Before making changes, read: auto-formats staged Go files and, when any Go file is staged, regenerates `internal/api/openapi.json` and `docs/schema/openapi.json` from the live supervisor. The hook stages both spec copies so the -committed spec never drifts from what the server actually serves. +committed spec never drifts from what the server actually serves. It also +runs the fast CI-equivalent gates for local changes: `make lint`, +`make vet`, and `make test` for Go changes, and `make check-docs` for +Markdown/docs/spec changes. **Dashboard SPA.** The dashboard at `cmd/gc/dashboard/web/` is a TypeScript SPA that talks directly to the supervisor's OpenAPI-typed @@ -32,9 +35,14 @@ endpoints. When `internal/api/openapi.json` or files under spec) and rebuilds `cmd/gc/dashboard/web/dist/` (the compiled bundle that the Go static server embeds via `go:embed`). The hook needs Node / npm on your PATH; if npm is missing, the hook warns and -skips the rebuild (CI enforces it). Run `make dashboard-dev` to +skips the rebuild (CI enforces it). The hook runs dashboard typecheck, +Vitest, and production build for dashboard/API-schema changes. Run +`make dashboard-dev` to iterate with Vite HMR, `make dashboard-build` to produce a fresh -bundle, `make dashboard-check` for typecheck + build + test. +bundle, `make dashboard-check` for typecheck + build + test. For +dashboard or API-schema changes, also smoke the built app with +`npm run preview -- --host 127.0.0.1 --port ` from +`cmd/gc/dashboard/web/` and load the served page before pushing. ## Development Workflow diff --git a/Makefile b/Makefile index 2f10834391..60cdcf50c5 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ LDFLAGS := -X main.version=$(VERSION) \ -X main.commit=$(COMMIT) \ -X main.date=$(BUILD_TIME) -.PHONY: build check check-all check-bd check-docker check-docs check-dolt check-version-tag lint fmt-check fmt vet test test-cmd-gc-process test-worker-core test-worker-core-phase2 test-worker-core-phase2-real-transport test-worker-inference-phase3 test-acceptance test-acceptance-b test-acceptance-c test-acceptance-all test-tutorial-goldens test-tutorial-regression test-tutorial test-integration test-integration-shards test-integration-shards-cover test-integration-packages test-integration-packages-cover test-integration-review-formulas test-integration-review-formulas-cover test-integration-review-formulas-basic test-integration-review-formulas-basic-cover test-integration-review-formulas-retries test-integration-review-formulas-retries-cover test-integration-review-formulas-recovery test-integration-review-formulas-recovery-cover test-integration-bdstore test-integration-bdstore-cover test-integration-rest test-integration-rest-cover test-integration-rest-smoke test-integration-rest-smoke-cover test-integration-rest-full test-integration-rest-full-cover test-mcp-mail test-docker test-k8s test-cover cover install install-tools install-buildx setup clean generate check-schema docker-base docker-agent docker-controller docs-dev +.PHONY: build check check-all check-bd check-docker check-docs check-dolt check-version-tag lint fmt-check fmt vet test test-fsys-darwin-compile test-cmd-gc-process test-worker-core test-worker-core-phase2 test-worker-core-phase2-real-transport test-worker-inference-phase3 test-acceptance test-acceptance-b test-acceptance-c test-acceptance-all test-tutorial-goldens test-tutorial-regression test-tutorial test-integration test-integration-shards test-integration-shards-cover test-integration-packages test-integration-packages-cover test-integration-review-formulas test-integration-review-formulas-cover test-integration-review-formulas-basic test-integration-review-formulas-basic-cover test-integration-review-formulas-retries test-integration-review-formulas-retries-cover test-integration-review-formulas-recovery test-integration-review-formulas-recovery-cover test-integration-bdstore test-integration-bdstore-cover test-integration-rest test-integration-rest-cover test-integration-rest-smoke test-integration-rest-smoke-cover test-integration-rest-full test-integration-rest-full-cover test-mcp-mail test-docker test-k8s test-cover cover install install-tools install-buildx setup clean generate check-schema docker-base docker-agent docker-controller docs-dev dashboard-smoke ## build: compile gc binary with version metadata build: @@ -163,15 +163,22 @@ TEST_ENV = env -i \ ## test: run fast unit tests (skip integration-tagged and GC_FAST_UNIT-gated process tests) ## The skipped cmd/gc process-backed scenarios remain covered by -## `make test-cmd-gc-process` locally and the CI `test-integration-packages` shard. +## `make test-cmd-gc-process` locally and the CI `cmd/gc process suite` job. ## Wrapped in $(TEST_ENV) — see comment above for why. -test: +test: test-fsys-darwin-compile $(TEST_ENV) GC_FAST_UNIT=1 go test ./... +## test-fsys-darwin-compile: cross-compile internal/fsys for macOS so +## unix.Stat_t field-type regressions fail in the default fast test path. +test-fsys-darwin-compile: + @tmp=$$(mktemp -d); \ + trap 'rm -rf "$$tmp"' EXIT; \ + $(TEST_ENV) GOOS=darwin GOARCH=arm64 go test -c -o "$$tmp/fsys.test" ./internal/fsys + ## test-cmd-gc-process: run the full non-short cmd/gc suite, including the ## process-backed lifecycle coverage routed out of the default fast loop test-cmd-gc-process: - $(TEST_ENV) GC_FAST_UNIT=0 go test ./cmd/gc + $(TEST_ENV) GC_FAST_UNIT=0 go test -count=1 -timeout 20m ./cmd/gc ## test-worker-core: run deterministic worker transcript and continuation conformance test-worker-core: @@ -348,8 +355,8 @@ UNIT_COVER_PKGS := $(shell go list -f '{{if or .TestGoFiles .XTestGoFiles}}{{.Im ## test-cover: run fast unit-test coverage without the integration-tagged package sweep ## The skipped cmd/gc process-backed scenarios remain covered by -## `make test-cmd-gc-process` locally and the CI `test-integration-packages` shard. -test-cover: +## `make test-cmd-gc-process` locally and the CI `cmd/gc process suite` job. +test-cover: test-fsys-darwin-compile $(TEST_ENV) GC_FAST_UNIT=1 go test -timeout 8m -coverprofile=coverage.txt $(UNIT_COVER_PKGS) ## cover: run tests and show coverage report @@ -415,6 +422,22 @@ dashboard-check: dashboard-build cd cmd/gc/dashboard/web && npm run typecheck $(TEST_ENV) go test ./cmd/gc/dashboard/... +## dashboard-smoke: serve the built SPA bundle via Vite preview and verify it responds +dashboard-smoke: dashboard-build + @PORT=$$(python3 -c 'import socket; sock = socket.socket(); sock.bind(("127.0.0.1", 0)); print(sock.getsockname()[1]); sock.close()'); \ + LOG=$$(mktemp); \ + ( cd cmd/gc/dashboard/web && exec npm run preview -- --host 127.0.0.1 --strictPort --port $$PORT >"$$LOG" 2>&1 ) & \ + PID=$$!; \ + trap 'kill $$PID >/dev/null 2>&1 || true; wait $$PID >/dev/null 2>&1 || true; rm -f "$$LOG"' EXIT INT TERM; \ + for attempt in $$(seq 1 40); do \ + if curl -fsS "http://127.0.0.1:$$PORT/" >/dev/null; then \ + exit 0; \ + fi; \ + sleep 0.25; \ + done; \ + cat "$$LOG" >&2; \ + exit 1 + ## dashboard-ci: rebuild the SPA bundle and fail if the tracked dist/ is stale. ## Used by CI to enforce that cmd/gc/dashboard/web/dist/ matches the source. dashboard-ci: dashboard-check diff --git a/RELEASING.md b/RELEASING.md index e37bab072e..b70ae40a89 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -5,7 +5,7 @@ | Channel | Mechanism | Automatic? | |---------|-----------|------------| | **GitHub Release** | GoReleaser via `release.yml` on tag push | Yes | -| **Homebrew tap** (`gastownhall/gascity`) | GoReleaser `brews:` block writes to the tap on tag push | Yes | +| **Homebrew tap** (`gastownhall/gascity`) | `release.yml` writes an asset-based formula after archives upload | Yes | | **Homebrew core** (`Homebrew/homebrew-core`) | BrewTestBot autobump, once listed | Yes (~3h delay) | The homebrew-core submission is [in progress](https://github.com/Homebrew/homebrew-core). Until it lands and is added to the autobump list, users install via `brew install gastownhall/gascity/gascity`. @@ -45,7 +45,8 @@ Version numbers live **only** in the git tag — there is no `Version` constant 1. **Reject `replace` directives in `go.mod`** — they break `go install ...@latest` and bottle builds in homebrew-core. 2. **`make check-version-tag`** — asserts the tag is a clean `vMAJOR.MINOR.PATCH` with no pre-release suffix. RC/beta tags will fail the release. Pre-release tags should be cut on a dedicated branch or not trigger this workflow. -3. **GoReleaser** — builds binaries for linux/darwin × amd64/arm64, creates the GitHub Release with grouped changelog (`feat:` → Features, `fix:` → Bug Fixes, others → Others), and writes the Homebrew tap formula via the `brews:` block in `.goreleaser.yml`. +3. **GoReleaser** — builds binaries for linux/darwin × amd64/arm64 and creates the GitHub Release with grouped changelog (`feat:` → Features, `fix:` → Bug Fixes, others → Others). +4. **Homebrew tap update** — downloads the published checksums and writes an asset-based formula to `gastownhall/homebrew-gascity`. Forks skip publish/announce steps automatically via the `--skip=publish --skip=announce` flag (the workflow checks `github.repository != 'gastownhall/gascity'`). @@ -58,9 +59,21 @@ grep '^replace' go.mod # should print nothing ## Homebrew tap (`gastownhall/gascity`) -The GoReleaser `brews:` block automatically overwrites `Formula/gascity.rb` in the `gastownhall/homebrew-gascity` repo on every tag push, using `HOMEBREW_TAP_TOKEN`. No manual action required. +The release workflow automatically overwrites `Formula/gascity.rb` in the `gastownhall/homebrew-gascity` repo on every tag push. It prefers the GitHub App credentials `HOMEBREW_TAP_APP_ID` and `HOMEBREW_TAP_APP_PRIVATE_KEY`, and falls back to the legacy `HOMEBREW_TAP_TOKEN` while the app rollout is in progress. -**This section will change when homebrew-core lands.** The `brews:` block will be removed, the tap will be deprecated, and releases will flow only through the source-built formula in homebrew-core. +The tap formula installs prebuilt release assets, so users do not need Go or a source build: + +```bash +brew install gastownhall/gascity/gascity +``` + +The intended long-term user-facing Homebrew path is homebrew-core: + +```bash +brew install gascity +``` + +Until the core formula lands, the tap is the public install path. After core lands, keep the tap available for emergency updates while normal releases flow through homebrew-core. ## Homebrew core (planned) @@ -80,7 +93,7 @@ Manual `brew bump-formula-pr` is refused for autobump formulae. If the bot stall | `CHANGELOG.md` | `[Unreleased]` → `[X.Y.Z] - DATE` | `scripts/bump-version.sh` | | Git tag `vX.Y.Z` | Created and pushed | `scripts/bump-version.sh` | | GitHub Release page | Created with binaries + grouped changelog | GoReleaser in `release.yml` | -| `gastownhall/homebrew-gascity/Formula/gascity.rb` | `url` + `sha256` updated | GoReleaser in `release.yml` | +| `gastownhall/homebrew-gascity/Formula/gascity.rb` | asset URLs + `sha256` updated | `update-homebrew-formula` in `release.yml` | ## Troubleshooting @@ -98,7 +111,7 @@ Check `.github/workflows/release.yml` still matches `tags: v*`. Verify the tag w ### Tap formula not updated -Check `HOMEBREW_TAP_TOKEN` in repo secrets. It needs `contents: write` on `gastownhall/homebrew-gascity`. The workflow logs will show the exact error. +Check the Homebrew tap credential in repo secrets. Preferred: `HOMEBREW_TAP_APP_ID` and `HOMEBREW_TAP_APP_PRIVATE_KEY` for a GitHub App installed on `gastownhall/homebrew-gascity` with contents write. Legacy fallback: `HOMEBREW_TAP_TOKEN` with contents write on the tap. The workflow logs will show the exact error. ### Homebrew shows old version after a release diff --git a/cmd/gc/api_state.go b/cmd/gc/api_state.go index d0ff7fcb26..db26edae92 100644 --- a/cmd/gc/api_state.go +++ b/cmd/gc/api_state.go @@ -3,6 +3,7 @@ package main import ( "context" "encoding/json" + "errors" "fmt" "io" "log" @@ -10,6 +11,7 @@ import ( "path/filepath" "strings" "sync" + "sync/atomic" "time" "github.com/gastownhall/gascity/internal/api" @@ -45,11 +47,19 @@ type controllerState struct { startedAt time.Time ct crashTracker // nil if crash tracking disabled pokeCh chan struct{} // nil when poke is not available; triggers immediate reconciler tick + configDirty *atomic.Bool // optional dirty flag shared with the reconciler reload path services workspacesvc.Registry extmsgSvc *extmsg.Services adapterReg *extmsg.AdapterRegistry } +type configMutationSnapshot struct { + cityPath string + files map[string][]byte + existed map[string]bool + agentFiles map[string]struct{} +} + // newControllerState creates a controllerState with per-rig stores. // BdStores are wrapped with CachingStore for in-memory reads. func newControllerState( @@ -120,6 +130,9 @@ func wrapWithCachingStore(ctx context.Context, store beads.Store, ep events.Prov if err := cs.PrimeActive(); err != nil { log.Printf("caching-store: pre-prime failed: %v", err) } + if ctx.Done() == nil { + return cs + } // Full prime runs async — backfills remaining beads for List() // callers (convergence reconcile, sweep, API handlers). go func() { @@ -236,11 +249,6 @@ func (cs *controllerState) applyBeadEventToStores(evt events.Event) { if len(evt.Payload) == 0 { return } - // Skip events we emitted ourselves (reconciler-detected changes). - if evt.Actor == "cache-reconcile" { - return - } - cs.mu.RLock() stores := make([]beads.Store, 0, len(cs.beadStores)+1) for _, s := range cs.beadStores { @@ -256,7 +264,9 @@ func (cs *controllerState) applyBeadEventToStores(evt events.Event) { cached.ApplyEvent(evt.Type, evt.Payload) } } - cs.Poke() + if evt.Actor != "cache-reconcile" { + cs.Poke() + } } // update replaces the config, session provider, and reopens stores. @@ -449,20 +459,24 @@ func (cs *controllerState) Orders() []orders.Order { // EnableOrder creates or updates an override with enabled=true. func (cs *controllerState) EnableOrder(name, rig string) error { enabled := true - return cs.editor.MergeOrderOverride(config.OrderOverride{ - Name: name, - Rig: rig, - Enabled: &enabled, + return cs.mutateAndPoke(func() error { + return cs.editor.MergeOrderOverride(config.OrderOverride{ + Name: name, + Rig: rig, + Enabled: &enabled, + }) }) } // DisableOrder creates or updates an override with enabled=false. func (cs *controllerState) DisableOrder(name, rig string) error { enabled := false - return cs.editor.MergeOrderOverride(config.OrderOverride{ - Name: name, - Rig: rig, - Enabled: &enabled, + return cs.mutateAndPoke(func() error { + return cs.editor.MergeOrderOverride(config.OrderOverride{ + Name: name, + Rig: rig, + Enabled: &enabled, + }) }) } @@ -511,107 +525,256 @@ func (cs *controllerState) ResumeCity() error { // CreateAgent adds a new agent to city.toml. func (cs *controllerState) CreateAgent(a config.Agent) error { - return cs.editor.CreateAgent(a) + return cs.mutateAndPoke(func() error { + return cs.editor.CreateAgent(a) + }) } // UpdateAgent partially updates an existing agent definition in city.toml. func (cs *controllerState) UpdateAgent(name string, patch api.AgentUpdate) error { - return cs.editor.UpdateAgent(name, configedit.AgentUpdate{ - Provider: patch.Provider, - Scope: patch.Scope, - Suspended: patch.Suspended, + return cs.mutateAndPoke(func() error { + return cs.editor.UpdateAgent(name, configedit.AgentUpdate{ + Provider: patch.Provider, + Scope: patch.Scope, + Suspended: patch.Suspended, + }) }) } // DeleteAgent removes an agent from city.toml. func (cs *controllerState) DeleteAgent(name string) error { - return cs.editor.DeleteAgent(name) + return cs.mutateAndPoke(func() error { + return cs.editor.DeleteAgent(name) + }) } // CreateRig adds a new rig to city.toml. func (cs *controllerState) CreateRig(r config.Rig) error { - return cs.editor.CreateRig(r) + return cs.mutateAndPoke(func() error { + return cs.editor.CreateRig(r) + }) } // UpdateRig partially updates a rig in city.toml. func (cs *controllerState) UpdateRig(name string, patch api.RigUpdate) error { - return cs.editor.UpdateRig(name, configedit.RigUpdate{ - Path: patch.Path, - Prefix: patch.Prefix, - Suspended: patch.Suspended, + return cs.mutateAndPoke(func() error { + return cs.editor.UpdateRig(name, configedit.RigUpdate{ + Path: patch.Path, + Prefix: patch.Prefix, + Suspended: patch.Suspended, + }) }) } // DeleteRig removes a rig from city.toml. func (cs *controllerState) DeleteRig(name string) error { - return cs.editor.DeleteRig(name) + return cs.mutateAndPoke(func() error { + return cs.editor.DeleteRig(name) + }) } // CreateProvider adds a new city-level provider to city.toml. func (cs *controllerState) CreateProvider(name string, spec config.ProviderSpec) error { - return cs.editor.CreateProvider(name, spec) + return cs.mutateAndPoke(func() error { + return cs.editor.CreateProvider(name, spec) + }) } // UpdateProvider partially updates an existing city-level provider. func (cs *controllerState) UpdateProvider(name string, patch api.ProviderUpdate) error { - return cs.editor.UpdateProvider(name, configedit.ProviderUpdate{ - DisplayName: patch.DisplayName, - Base: patch.Base, - Command: patch.Command, - Args: patch.Args, - ArgsAppend: patch.ArgsAppend, - PromptMode: patch.PromptMode, - PromptFlag: patch.PromptFlag, - ReadyDelayMs: patch.ReadyDelayMs, - Env: patch.Env, - OptionsSchemaMerge: patch.OptionsSchemaMerge, - OptionsSchema: patch.OptionsSchema, + return cs.mutateAndPoke(func() error { + return cs.editor.UpdateProvider(name, configedit.ProviderUpdate{ + DisplayName: patch.DisplayName, + Base: patch.Base, + Command: patch.Command, + ACPCommand: patch.ACPCommand, + Args: patch.Args, + ACPArgs: patch.ACPArgs, + ArgsAppend: patch.ArgsAppend, + PromptMode: patch.PromptMode, + PromptFlag: patch.PromptFlag, + ReadyDelayMs: patch.ReadyDelayMs, + Env: patch.Env, + OptionsSchemaMerge: patch.OptionsSchemaMerge, + OptionsSchema: patch.OptionsSchema, + }) }) } // DeleteProvider removes a city-level provider from city.toml. func (cs *controllerState) DeleteProvider(name string) error { - return cs.editor.DeleteProvider(name) + return cs.mutateAndPoke(func() error { + return cs.editor.DeleteProvider(name) + }) } // SetAgentPatch creates or replaces an agent patch in city.toml. func (cs *controllerState) SetAgentPatch(patch config.AgentPatch) error { - return cs.editor.SetAgentPatch(patch) + return cs.mutateAndPoke(func() error { + return cs.editor.SetAgentPatch(patch) + }) } // DeleteAgentPatch removes an agent patch from city.toml. func (cs *controllerState) DeleteAgentPatch(name string) error { - return cs.editor.DeleteAgentPatch(name) + return cs.mutateAndPoke(func() error { + return cs.editor.DeleteAgentPatch(name) + }) } // SetRigPatch creates or replaces a rig patch in city.toml. func (cs *controllerState) SetRigPatch(patch config.RigPatch) error { - return cs.editor.SetRigPatch(patch) + return cs.mutateAndPoke(func() error { + return cs.editor.SetRigPatch(patch) + }) } // DeleteRigPatch removes a rig patch from city.toml. func (cs *controllerState) DeleteRigPatch(name string) error { - return cs.editor.DeleteRigPatch(name) + return cs.mutateAndPoke(func() error { + return cs.editor.DeleteRigPatch(name) + }) } // SetProviderPatch creates or replaces a provider patch in city.toml. func (cs *controllerState) SetProviderPatch(patch config.ProviderPatch) error { - return cs.editor.SetProviderPatch(patch) + return cs.mutateAndPoke(func() error { + return cs.editor.SetProviderPatch(patch) + }) } // DeleteProviderPatch removes a provider patch from city.toml. func (cs *controllerState) DeleteProviderPatch(name string) error { - return cs.editor.DeleteProviderPatch(name) + return cs.mutateAndPoke(func() error { + return cs.editor.DeleteProviderPatch(name) + }) +} + +func captureConfigMutationSnapshot(cityPath string) (*configMutationSnapshot, error) { + snapshot := &configMutationSnapshot{ + cityPath: cityPath, + files: make(map[string][]byte), + existed: make(map[string]bool), + agentFiles: make(map[string]struct{}), + } + + capture := func(path string) error { + data, err := os.ReadFile(path) + switch { + case err == nil: + snapshot.files[path] = data + snapshot.existed[path] = true + case os.IsNotExist(err): + snapshot.existed[path] = false + default: + return fmt.Errorf("reading %s: %w", path, err) + } + return nil + } + + for _, path := range []string{ + filepath.Join(cityPath, "city.toml"), + filepath.Join(cityPath, ".gc", "site.toml"), + } { + if err := capture(path); err != nil { + return nil, err + } + } + + agentFiles, err := filepath.Glob(filepath.Join(cityPath, "agents", "*", "agent.toml")) + if err != nil { + return nil, fmt.Errorf("listing agent overrides: %w", err) + } + for _, path := range agentFiles { + snapshot.agentFiles[path] = struct{}{} + if err := capture(path); err != nil { + return nil, err + } + } + + return snapshot, nil +} + +func (s *configMutationSnapshot) restore() error { + var restoreErr error + + currentAgentFiles, err := filepath.Glob(filepath.Join(s.cityPath, "agents", "*", "agent.toml")) + if err != nil { + restoreErr = errors.Join(restoreErr, fmt.Errorf("listing current agent overrides: %w", err)) + } else { + for _, path := range currentAgentFiles { + if _, existed := s.agentFiles[path]; existed { + continue + } + if err := os.Remove(path); err != nil && !os.IsNotExist(err) { + restoreErr = errors.Join(restoreErr, fmt.Errorf("removing %s: %w", path, err)) + } + } + } + + for path, existed := range s.existed { + if !existed { + if err := os.Remove(path); err != nil && !os.IsNotExist(err) { + restoreErr = errors.Join(restoreErr, fmt.Errorf("removing %s: %w", path, err)) + } + continue + } + if err := fsys.WriteFileAtomic(fsys.OSFS{}, path, s.files[path], 0o644); err != nil { + restoreErr = errors.Join(restoreErr, fmt.Errorf("restoring %s: %w", path, err)) + } + } + + return restoreErr } func (cs *controllerState) mutateAndPoke(mutate func() error) error { + var snapshot *configMutationSnapshot + if cs.cityPath != "" { + var err error + snapshot, err = captureConfigMutationSnapshot(cs.cityPath) + if err != nil { + return fmt.Errorf("snapshotting current city config: %w", err) + } + } if err := mutate(); err != nil { return err } + if err := cs.refreshConfigSnapshot(); err != nil { + if snapshot != nil { + if restoreErr := snapshot.restore(); restoreErr != nil { + restoreFailure := fmt.Errorf("restoring previous city config: %w", restoreErr) + return fmt.Errorf("refreshing updated city config: %w", errors.Join(err, restoreFailure)) + } + } + return fmt.Errorf("refreshing updated city config: %w", err) + } + if cs.configDirty != nil { + cs.configDirty.Store(true) + } cs.Poke() return nil } +func (cs *controllerState) refreshConfigSnapshot() error { + if cs.cityPath == "" || cs.cfg == nil { + return nil + } + + tomlPath := filepath.Join(cs.cityPath, "city.toml") + nextCfg, _, err := config.LoadWithIncludes(fsys.OSFS{}, tomlPath, extraConfigFiles...) + if err != nil { + return fmt.Errorf("loading updated city config: %w", err) + } + applyFeatureFlags(nextCfg) + applyRuntimeCityIdentity(nextCfg, cs.cityName) + + cs.mu.RLock() + sp := cs.sp + cs.mu.RUnlock() + cs.update(nextCfg, sp) + return nil +} + // Poke signals the controller to trigger an immediate reconciler tick. // Non-blocking: if a poke is already pending, additional pokes are dropped. func (cs *controllerState) Poke() { diff --git a/cmd/gc/api_state_test.go b/cmd/gc/api_state_test.go index c0755f4f32..f78d033a58 100644 --- a/cmd/gc/api_state_test.go +++ b/cmd/gc/api_state_test.go @@ -7,8 +7,10 @@ import ( "path/filepath" "strings" "sync" + "sync/atomic" "testing" + "github.com/gastownhall/gascity/internal/api" "github.com/gastownhall/gascity/internal/beads" "github.com/gastownhall/gascity/internal/config" "github.com/gastownhall/gascity/internal/configedit" @@ -134,6 +136,178 @@ func TestControllerStateUpdate(t *testing.T) { } } +func TestControllerStateCreateRigPokesReconciler(t *testing.T) { + t.Setenv("GC_BEADS", "file") + + cityDir := t.TempDir() + if err := os.WriteFile(filepath.Join(cityDir, "city.toml"), []byte("[workspace]\nname = \"city1\"\n"), 0o644); err != nil { + t.Fatalf("write city.toml: %v", err) + } + cfg := &config.City{ + Workspace: config.Workspace{Name: "city1"}, + } + cs := newControllerState(context.Background(), cfg, runtime.NewFake(), events.NewFake(), "city1", cityDir) + cs.pokeCh = make(chan struct{}, 1) + cs.configDirty = &atomic.Bool{} + + if err := cs.CreateRig(config.Rig{Name: "rig1", Path: t.TempDir()}); err != nil { + t.Fatalf("CreateRig: %v", err) + } + + select { + case <-cs.pokeCh: + default: + t.Fatal("CreateRig did not poke the reconciler") + } + if !cs.configDirty.Load() { + t.Fatal("CreateRig did not mark config dirty") + } + if got := cs.Config(); got == nil || len(got.Rigs) != 1 || got.Rigs[0].Name != "rig1" { + t.Fatalf("Config() rigs = %+v, want in-memory rig snapshot to include rig1", got.Rigs) + } +} + +func TestControllerStateMutationRollsBackWhenRefreshFails(t *testing.T) { + t.Setenv("GC_BEADS", "file") + + cityDir := t.TempDir() + if err := os.WriteFile(filepath.Join(cityDir, "broken.toml"), []byte("["), 0o644); err != nil { + t.Fatalf("write broken include: %v", err) + } + + original := []byte("include = [\"broken.toml\"]\n\n[workspace]\nname = \"city1\"\n") + tomlPath := filepath.Join(cityDir, "city.toml") + if err := os.WriteFile(tomlPath, original, 0o644); err != nil { + t.Fatalf("write city.toml: %v", err) + } + + cfg := &config.City{ + Workspace: config.Workspace{Name: "city1"}, + } + cs := newControllerState(context.Background(), cfg, runtime.NewFake(), events.NewFake(), "city1", cityDir) + cs.pokeCh = make(chan struct{}, 1) + cs.configDirty = &atomic.Bool{} + + err := cs.CreateRig(config.Rig{Name: "rig1", Path: t.TempDir()}) + if err == nil { + t.Fatal("CreateRig should fail when refreshing the updated snapshot fails") + } + + restored, readErr := os.ReadFile(tomlPath) + if readErr != nil { + t.Fatalf("read restored city.toml: %v", readErr) + } + if string(restored) != string(original) { + t.Fatalf("city.toml = %q, want rollback to %q", restored, original) + } + if _, err := os.Stat(filepath.Join(cityDir, ".gc", "site.toml")); !os.IsNotExist(err) { + t.Fatalf(".gc/site.toml stat err = %v, want file removed on rollback", err) + } + + select { + case <-cs.pokeCh: + t.Fatal("CreateRig should not poke the reconciler after rollback") + default: + } + if cs.configDirty.Load() { + t.Fatal("CreateRig should not mark config dirty after rollback") + } + if got := cs.Config(); got == nil || len(got.Rigs) != 0 { + t.Fatalf("Config() rigs = %+v, want rollback to preserve in-memory config", got.Rigs) + } +} + +func TestControllerStateMutationRollsBackAgentOverrideWhenRefreshFails(t *testing.T) { + t.Setenv("GC_BEADS", "file") + + cityDir := t.TempDir() + if err := os.WriteFile(filepath.Join(cityDir, "broken.toml"), []byte("["), 0o644); err != nil { + t.Fatalf("write broken include: %v", err) + } + if err := os.WriteFile(filepath.Join(cityDir, "pack.toml"), []byte("[pack]\nname = \"city1\"\nschema = 2\n"), 0o644); err != nil { + t.Fatalf("write pack.toml: %v", err) + } + agentDir := filepath.Join(cityDir, "agents", "worker") + if err := os.MkdirAll(agentDir, 0o755); err != nil { + t.Fatalf("mkdir agent dir: %v", err) + } + if err := os.WriteFile(filepath.Join(agentDir, "prompt.template.md"), []byte("You are the worker.\n"), 0o644); err != nil { + t.Fatalf("write prompt template: %v", err) + } + + original := []byte("include = [\"broken.toml\"]\n\n[workspace]\nname = \"city1\"\n") + tomlPath := filepath.Join(cityDir, "city.toml") + if err := os.WriteFile(tomlPath, original, 0o644); err != nil { + t.Fatalf("write city.toml: %v", err) + } + + cs := newControllerState(context.Background(), &config.City{ + Workspace: config.Workspace{Name: "city1"}, + }, runtime.NewFake(), events.NewFake(), "city1", cityDir) + cs.pokeCh = make(chan struct{}, 1) + cs.configDirty = &atomic.Bool{} + + err := cs.SuspendAgent("worker") + if err == nil { + t.Fatal("SuspendAgent should fail when refreshing the updated snapshot fails") + } + + if _, err := os.Stat(filepath.Join(agentDir, "agent.toml")); !os.IsNotExist(err) { + t.Fatalf("agent.toml stat err = %v, want file removed on rollback", err) + } + restored, readErr := os.ReadFile(tomlPath) + if readErr != nil { + t.Fatalf("read restored city.toml: %v", readErr) + } + if string(restored) != string(original) { + t.Fatalf("city.toml = %q, want rollback to %q", restored, original) + } + if cs.configDirty.Load() { + t.Fatal("SuspendAgent should not mark config dirty after rollback") + } +} + +func TestControllerStateAppliesCacheReconcileBeadEventsToStores(t *testing.T) { + backing := beads.NewMemStore() + created, err := backing.Create(beads.Bead{Title: "root"}) + if err != nil { + t.Fatalf("Create: %v", err) + } + cached := beads.NewCachingStoreForTest(backing, nil) + if err := cached.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + + updated := created + updated.Status = "in_progress" + payload, err := json.Marshal(updated) + if err != nil { + t.Fatalf("marshal updated bead: %v", err) + } + cs := &controllerState{ + beadStores: map[string]beads.Store{"alpha": cached}, + pokeCh: make(chan struct{}, 1), + } + + cs.applyBeadEventToStores(events.Event{ + Type: events.BeadUpdated, + Actor: "cache-reconcile", + Subject: created.ID, + Payload: payload, + }) + + items, err := cached.List(beads.ListQuery{AllowScan: true}) + if err != nil { + t.Fatalf("List: %v", err) + } + if len(items) != 1 || items[0].ID != created.ID { + t.Fatalf("cached items = %+v, want only %s", items, created.ID) + } + if items[0].Status != "in_progress" { + t.Fatalf("status after cache-reconcile event = %q, want in_progress", items[0].Status) + } +} + func TestControllerStateBuildStoresUsesScopeLocalFileStores(t *testing.T) { t.Setenv("GC_BEADS", "file") @@ -649,6 +823,247 @@ func TestControllerStateMutationsPokeController(t *testing.T) { } }, }, + { + name: "enable order", + mutate: func(cs *controllerState) error { + return cs.EnableOrder("nightly", "rig1") + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Orders.Overrides) != 1 || cfg.Orders.Overrides[0].Name != "nightly" || cfg.Orders.Overrides[0].Rig != "rig1" { + t.Fatalf("order overrides = %+v, want nightly/rig1", cfg.Orders.Overrides) + } + if cfg.Orders.Overrides[0].Enabled == nil || !*cfg.Orders.Overrides[0].Enabled { + t.Fatalf("order override enabled = %v, want true", cfg.Orders.Overrides[0].Enabled) + } + }, + }, + { + name: "disable order", + mutate: func(cs *controllerState) error { + return cs.DisableOrder("nightly", "rig1") + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Orders.Overrides) != 1 || cfg.Orders.Overrides[0].Enabled == nil || *cfg.Orders.Overrides[0].Enabled { + t.Fatalf("order overrides = %+v, want disabled nightly override", cfg.Orders.Overrides) + } + }, + }, + { + name: "create agent", + mutate: func(cs *controllerState) error { + return cs.CreateAgent(config.Agent{Name: "helper", Dir: "rig1", Provider: "codex"}) + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Agents) != 2 { + t.Fatalf("agents = %+v, want two", cfg.Agents) + } + if cfg.Agents[1].QualifiedName() != "rig1/helper" || cfg.Agents[1].Provider != "codex" { + t.Fatalf("created agent = %+v, want rig1/helper with codex provider", cfg.Agents[1]) + } + }, + }, + { + name: "update agent", + mutate: func(cs *controllerState) error { + return cs.UpdateAgent("rig1/worker", api.AgentUpdate{Provider: "codex", Scope: "rig", Suspended: boolPtr(true)}) + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if cfg.Agents[0].Provider != "codex" || cfg.Agents[0].Scope != "rig" || !cfg.Agents[0].Suspended { + t.Fatalf("updated agent = %+v, want provider/scope/suspended", cfg.Agents[0]) + } + }, + }, + { + name: "delete agent", + mutate: func(cs *controllerState) error { + return cs.DeleteAgent("rig1/worker") + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Agents) != 0 { + t.Fatalf("agents = %+v, want none", cfg.Agents) + } + }, + }, + { + name: "create rig", + mutate: func(cs *controllerState) error { + return cs.CreateRig(config.Rig{Name: "rig2", Path: t.TempDir(), Prefix: "r2"}) + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Rigs) != 2 { + t.Fatalf("rigs = %+v, want two", cfg.Rigs) + } + if cfg.Rigs[1].Name != "rig2" || cfg.Rigs[1].Prefix != "r2" { + t.Fatalf("created rig = %+v, want rig2/r2", cfg.Rigs[1]) + } + }, + }, + { + name: "update rig", + mutate: func(cs *controllerState) error { + return cs.UpdateRig("rig1", api.RigUpdate{Path: t.TempDir(), Prefix: "rg", Suspended: boolPtr(true)}) + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if cfg.Rigs[0].Prefix != "rg" || !cfg.Rigs[0].Suspended { + t.Fatalf("updated rig = %+v, want prefix/suspended", cfg.Rigs[0]) + } + }, + }, + { + name: "delete rig", + mutate: func(cs *controllerState) error { + return cs.DeleteRig("rig1") + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Rigs) != 0 || len(cfg.Agents) != 0 { + t.Fatalf("config after DeleteRig: rigs=%+v agents=%+v, want none", cfg.Rigs, cfg.Agents) + } + }, + }, + { + name: "create provider", + mutate: func(cs *controllerState) error { + return cs.CreateProvider("codex-local", config.ProviderSpec{Command: "codex", PromptMode: "arg"}) + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + spec, ok := cfg.Providers["codex-local"] + if !ok || spec.Command != "codex" || spec.PromptMode != "arg" { + t.Fatalf("providers = %+v, want codex-local provider", cfg.Providers) + } + }, + }, + { + name: "update provider", + initial: func(cfg *config.City) { + cfg.Providers = map[string]config.ProviderSpec{"codex-local": {Command: "codex"}} + }, + mutate: func(cs *controllerState) error { + return cs.UpdateProvider("codex-local", api.ProviderUpdate{ + DisplayName: stringPtr("Codex Local"), + Command: stringPtr("codex-wrapper"), + Args: []string{"--quiet"}, + PromptMode: stringPtr("flag"), + PromptFlag: stringPtr("--prompt"), + ReadyDelayMs: intPtr(25), + Env: map[string]string{"GC_TEST": "1"}, + }) + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + spec := cfg.Providers["codex-local"] + if spec.DisplayName != "Codex Local" || spec.Command != "codex-wrapper" || spec.PromptMode != "flag" || spec.PromptFlag != "--prompt" || spec.ReadyDelayMs != 25 { + t.Fatalf("updated provider = %+v, want scalar updates", spec) + } + if len(spec.Args) != 1 || spec.Args[0] != "--quiet" || spec.Env["GC_TEST"] != "1" { + t.Fatalf("updated provider args/env = args:%+v env:%+v, want replacement args and merged env", spec.Args, spec.Env) + } + }, + }, + { + name: "delete provider", + initial: func(cfg *config.City) { + cfg.Providers = map[string]config.ProviderSpec{"codex-local": {Command: "codex"}} + }, + mutate: func(cs *controllerState) error { + return cs.DeleteProvider("codex-local") + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Providers) != 0 { + t.Fatalf("providers = %+v, want none", cfg.Providers) + } + }, + }, + { + name: "set agent patch", + mutate: func(cs *controllerState) error { + return cs.SetAgentPatch(config.AgentPatch{Dir: "rig1", Name: "worker", Suspended: boolPtr(true)}) + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Patches.Agents) != 1 || cfg.Patches.Agents[0].Suspended == nil || !*cfg.Patches.Agents[0].Suspended { + t.Fatalf("agent patches = %+v, want suspended patch", cfg.Patches.Agents) + } + }, + }, + { + name: "delete agent patch", + initial: func(cfg *config.City) { + cfg.Patches.Agents = []config.AgentPatch{{Dir: "rig1", Name: "worker", Suspended: boolPtr(true)}} + }, + mutate: func(cs *controllerState) error { + return cs.DeleteAgentPatch("rig1/worker") + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Patches.Agents) != 0 { + t.Fatalf("agent patches = %+v, want none", cfg.Patches.Agents) + } + }, + }, + { + name: "set rig patch", + mutate: func(cs *controllerState) error { + return cs.SetRigPatch(config.RigPatch{Name: "rig1", Prefix: stringPtr("rp")}) + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Patches.Rigs) != 1 || cfg.Patches.Rigs[0].Prefix == nil || *cfg.Patches.Rigs[0].Prefix != "rp" { + t.Fatalf("rig patches = %+v, want prefix patch", cfg.Patches.Rigs) + } + }, + }, + { + name: "delete rig patch", + initial: func(cfg *config.City) { + cfg.Patches.Rigs = []config.RigPatch{{Name: "rig1", Prefix: stringPtr("rp")}} + }, + mutate: func(cs *controllerState) error { + return cs.DeleteRigPatch("rig1") + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Patches.Rigs) != 0 { + t.Fatalf("rig patches = %+v, want none", cfg.Patches.Rigs) + } + }, + }, + { + name: "set provider patch", + mutate: func(cs *controllerState) error { + return cs.SetProviderPatch(config.ProviderPatch{Name: "codex-local", Command: stringPtr("codex-wrapper")}) + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Patches.Providers) != 1 || cfg.Patches.Providers[0].Command == nil || *cfg.Patches.Providers[0].Command != "codex-wrapper" { + t.Fatalf("provider patches = %+v, want command patch", cfg.Patches.Providers) + } + }, + }, + { + name: "delete provider patch", + initial: func(cfg *config.City) { + cfg.Patches.Providers = []config.ProviderPatch{{Name: "codex-local", Command: stringPtr("codex-wrapper")}} + }, + mutate: func(cs *controllerState) error { + return cs.DeleteProviderPatch("codex-local") + }, + verify: func(t *testing.T, cfg *config.City) { + t.Helper() + if len(cfg.Patches.Providers) != 0 { + t.Fatalf("provider patches = %+v, want none", cfg.Patches.Providers) + } + }, + }, } for _, tc := range cases { @@ -678,6 +1093,9 @@ func TestControllerStateMutationsPokeController(t *testing.T) { default: t.Fatal("expected controller mutation to poke reconciler") } + if cs.configDirty == nil || !cs.configDirty.Load() { + t.Fatal("expected controller mutation to mark config dirty") + } got, err := config.Load(fsys.OSFS{}, tomlPath) if err != nil { @@ -767,8 +1185,9 @@ func newControllerStateMutationHarness(t *testing.T) (*controllerState, string) } return &controllerState{ - editor: configedit.NewEditor(fsys.OSFS{}, tomlPath), - pokeCh: make(chan struct{}, 1), + editor: configedit.NewEditor(fsys.OSFS{}, tomlPath), + pokeCh: make(chan struct{}, 1), + configDirty: &atomic.Bool{}, }, tomlPath } diff --git a/cmd/gc/beads_provider_lifecycle.go b/cmd/gc/beads_provider_lifecycle.go index 40a1fbb52c..f7ce2f6284 100644 --- a/cmd/gc/beads_provider_lifecycle.go +++ b/cmd/gc/beads_provider_lifecycle.go @@ -428,6 +428,9 @@ func ensureBeadsProvider(cityPath string) error { // Called by gc stop after agents have been terminated. // For exec providers, fires "stop". For file providers, always available. func shutdownBeadsProvider(cityPath string) error { + if cityUsesBdStoreContract(cityPath) && strings.TrimSpace(os.Getenv("GC_DOLT")) == "skip" { + return clearManagedDoltRuntimeStateIfOwned(cityPath) + } provider := beadsProvider(cityPath) if strings.HasPrefix(provider, "exec:") { if providerUsesBdStoreContract(provider) && isExternalDolt(cityPath) { diff --git a/cmd/gc/beads_provider_lifecycle_test.go b/cmd/gc/beads_provider_lifecycle_test.go index 0cee60408d..64ee4754dc 100644 --- a/cmd/gc/beads_provider_lifecycle_test.go +++ b/cmd/gc/beads_provider_lifecycle_test.go @@ -758,6 +758,31 @@ func TestShutdownBeadsProvider_bd_skip(t *testing.T) { } } +func TestShutdownBeadsProviderBdSkipClearsPublishedRuntimeState(t *testing.T) { + dir := t.TempDir() + if err := os.MkdirAll(filepath.Join(dir, ".gc"), 0o755); err != nil { + t.Fatal(err) + } + MaterializeBuiltinPacks(dir) //nolint:errcheck + if err := writeDoltState(dir, doltRuntimeState{ + Running: true, + PID: os.Getpid(), + Port: 33123, + DataDir: filepath.Join(dir, ".beads", "dolt"), + StartedAt: time.Now().UTC().Format(time.RFC3339), + }); err != nil { + t.Fatalf("writeDoltState: %v", err) + } + t.Setenv("GC_BEADS", "bd") + t.Setenv("GC_DOLT", "skip") + if err := shutdownBeadsProvider(dir); err != nil { + t.Fatalf("shutdownBeadsProvider() error = %v", err) + } + if _, err := os.Stat(managedDoltStatePath(dir)); !os.IsNotExist(err) { + t.Fatalf("published dolt runtime state still present, stat err = %v", err) + } +} + func TestCurrentDoltPortPrefersRuntimeState(t *testing.T) { cityDir := t.TempDir() if err := os.MkdirAll(filepath.Join(cityDir, ".gc", "runtime", "packs", "dolt"), 0o755); err != nil { @@ -3613,6 +3638,7 @@ esac } func TestGcBeadsBdInitBackfillsRepoIDMigrationWhenMetadataExistsWithoutProjectID(t *testing.T) { + skipSlowCmdGCTest(t, "runs the materialized gc-beads-bd init script with GC_BIN helper; run make test-cmd-gc-process for full coverage") cityPath := t.TempDir() if err := os.MkdirAll(filepath.Join(cityPath, ".gc"), 0o755); err != nil { t.Fatal(err) @@ -5967,13 +5993,36 @@ func TestGcBeadsBdStartConcurrentWaitPassesRemainingExistingManagedBudget(t *tes t.Fatal(err) } invocationFile := filepath.Join(t.TempDir(), "gc-invocation") + nowFile := filepath.Join(t.TempDir(), "gc-now-ms") fakeGC := filepath.Join(binDir, "gc") fakeGCScript := fmt.Sprintf(`#!/bin/sh set -eu invocation_file=%q +now_file=%q subcmd="$1 $2" shift 2 case "$subcmd" in + "dolt-state now-ms") + if [ -f "$now_file" ]; then + step=$(cat "$now_file") + else + step=0 + fi + case "$step" in + 0) + printf '1000000\n' + printf '1\n' > "$now_file" + ;; + 1) + printf '1000000\n' + printf '2\n' > "$now_file" + ;; + *) + printf '1001000\n' + printf '3\n' > "$now_file" + ;; + esac + ;; "dolt-state runtime-layout") while [ "$#" -gt 0 ]; do case "$1" in @@ -6044,7 +6093,7 @@ case "$subcmd" in exit 64 ;; esac -`, invocationFile, layout.PackStateDir, layout.DataDir, layout.LogFile, layout.StateFile, layout.PIDFile, layout.LockFile, layout.ConfigFile) +`, invocationFile, nowFile, layout.PackStateDir, layout.DataDir, layout.LogFile, layout.StateFile, layout.PIDFile, layout.LockFile, layout.ConfigFile) if err := os.WriteFile(fakeGC, []byte(fakeGCScript), 0o755); err != nil { t.Fatal(err) } @@ -6353,6 +6402,7 @@ func TestGcBeadsBdRecoverHelperPreservesReadOnlyWarning(t *testing.T) { } func TestManagedDoltConfigGoWriterMatchesShellFallbackSemantics(t *testing.T) { + skipSlowCmdGCTest(t, "starts the materialized gc-beads-bd shell fallback; run make test-cmd-gc-process for full coverage") cityPath := t.TempDir() if err := os.MkdirAll(filepath.Join(cityPath, ".gc"), 0o755); err != nil { t.Fatal(err) @@ -7763,6 +7813,7 @@ func TestNormalizeCanonicalBdScopeFilesMaterializesMissingMetadata(t *testing.T) } func TestGcBeadsBdStartFallsBackToShellManagedConfigWriterWhenGCBinUnset(t *testing.T) { + skipSlowCmdGCTest(t, "starts the materialized gc-beads-bd shell fallback; run make test-cmd-gc-process for full coverage") cityPath := t.TempDir() if err := os.MkdirAll(filepath.Join(cityPath, ".gc"), 0o755); err != nil { t.Fatal(err) diff --git a/cmd/gc/build_desired_state_test.go b/cmd/gc/build_desired_state_test.go index 21c24180f0..e7e54f1e6c 100644 --- a/cmd/gc/build_desired_state_test.go +++ b/cmd/gc/build_desired_state_test.go @@ -536,6 +536,7 @@ func TestBuildDesiredState_RoutedQueueDoesNotCreateOneSessionPerBead(t *testing. } func TestBuildDesiredState_MinZeroDefaultScaleCheckRoutedWorkCreatesPoolSession(t *testing.T) { + skipSlowCmdGCTest(t, "uses real bd subprocesses for routed-work scale checks; run make test-cmd-gc-process for full coverage") bdPath, err := findPreferredBinary("bd", "/home/ubuntu/.local/bin/bd") if err != nil { t.Skip("bd not installed") @@ -2207,6 +2208,7 @@ func TestBuildDesiredState_PoolCheckUsesExplicitRigPassword(t *testing.T) { } func TestBuildDesiredState_PoolCheckUsesManagedCityDoltPortWhenRigHasNoOverride(t *testing.T) { + skipSlowCmdGCTest(t, "uses a live managed-dolt port probe for scale_check coverage; run make test-cmd-gc-process for full coverage") t.Setenv("GC_BEADS", "bd") cityPath := t.TempDir() rigPath := filepath.Join(cityPath, "myrig") diff --git a/cmd/gc/city_registry.go b/cmd/gc/city_registry.go index f84d494f5c..ed1fbd7486 100644 --- a/cmd/gc/city_registry.go +++ b/cmd/gc/city_registry.go @@ -1,12 +1,17 @@ package main import ( + "context" + "io" + "os" "path/filepath" "sync" "sync/atomic" "time" "github.com/gastownhall/gascity/internal/api" + "github.com/gastownhall/gascity/internal/events" + "github.com/gastownhall/gascity/internal/supervisor" ) // cityView is a read-only projection of managedCity, built at snapshot time. @@ -172,6 +177,117 @@ func (r *cityRegistry) Snapshot() *citySnapshot { return r.snap.Load() } +// TransientCityEventProviders implements api.TransientCityEventSource +// so the supervisor-scope event multiplexer can surface events from +// every registered city's .gc/events.jsonl — including those that +// aren't yet in the Running set. Covers four cases uniformly: +// +// - Newly scaffolded: written to cities.toml by Scaffold, but the +// reconciler hasn't picked it up yet. Not in cityRegistry snap +// yet; discovered directly from the on-disk supervisor registry. +// - Pending: reconciler picked up, cityView exists in snap.all +// with Started=false. +// - In progress: reconciler is running prepareCityForSupervisor. +// - Failed: reconciler gave up; entry lives in initFailures. +// +// Reading cities.toml directly (not just snap.all) closes the race +// between Scaffold returning 202 and the reconciler tick picking up +// the city — a client that subscribes to /v0/events/stream +// immediately after POST /v0/city sees the new city's event file in +// the multiplexer without waiting for the reconciler. +// +// Best-effort: cities whose event file is missing or unreadable are +// simply skipped. +func (r *cityRegistry) TransientCityEventProviders() map[string]events.Provider { + snap := r.snap.Load() + // Collect non-Running cities known to the runtime registry. + paths := make(map[string]string, len(snap.all)) + for _, v := range snap.all { + if v == nil || v.Started { + continue + } + name := v.Name + if name == "" { + name = filepath.Base(v.Path) + } + paths[name] = v.Path + } + // Also read cities.toml directly so cities Scaffold just + // registered — but the reconciler hasn't processed yet — are + // visible. Running cities already covered by the main + // multiplexer loop (via ListCities); skip them here. + running := make(map[string]struct{}, len(snap.byName)) + for name, v := range snap.byName { + if v != nil && v.Started { + running[name] = struct{}{} + } + } + reg := supervisor.NewRegistry(supervisor.RegistryPath()) + if entries, err := reg.List(); err == nil { + for _, e := range entries { + name := e.EffectiveName() + if _, already := running[name]; already { + continue + } + if _, already := paths[name]; already { + continue + } + paths[name] = e.Path + } + } + + out := make(map[string]events.Provider, len(paths)) + for name, path := range paths { + evPath := filepath.Join(path, ".gc", "events.jsonl") + if _, err := os.Stat(evPath); err != nil { + continue + } + if _, err := events.ReadLatestSeq(evPath); err != nil { + continue + } + out[name] = transientCityEventProvider{path: evPath} + } + return out +} + +type transientCityEventProvider struct { + path string +} + +func (p transientCityEventProvider) Record(e events.Event) { + recorder, err := events.NewFileRecorder(p.path, io.Discard) + if err != nil { + return + } + recorder.Record(e) + recorder.Close() //nolint:errcheck // best-effort +} + +func (p transientCityEventProvider) List(filter events.Filter) ([]events.Event, error) { + return events.ReadFiltered(p.path, filter) +} + +func (p transientCityEventProvider) LatestSeq() (uint64, error) { + return events.ReadLatestSeq(p.path) +} + +func (p transientCityEventProvider) Watch(ctx context.Context, afterSeq uint64) (events.Watcher, error) { + recorder, err := events.NewFileRecorder(p.path, io.Discard) + if err != nil { + return nil, err + } + watcher, err := recorder.Watch(ctx, afterSeq) + recorder.Close() //nolint:errcheck // watcher only needs the path + if err != nil { + return nil, err + } + return watcher, nil +} + +func (transientCityEventProvider) Close() error { + return nil +} + // CityState returns the api.State for a named city, or nil if not found/not running. // Lock-free read from the atomic snapshot. func (r *cityRegistry) CityState(name string) api.State { diff --git a/cmd/gc/city_registry_test.go b/cmd/gc/city_registry_test.go index 341aea369d..a35623b3e8 100644 --- a/cmd/gc/city_registry_test.go +++ b/cmd/gc/city_registry_test.go @@ -1,9 +1,15 @@ package main import ( + "io" + "os" + "path/filepath" "sync" "testing" "time" + + "github.com/gastownhall/gascity/internal/events" + "github.com/gastownhall/gascity/internal/supervisor" ) func TestCityRegistryEmptySnapshot(t *testing.T) { @@ -223,6 +229,103 @@ func TestCityRegistryTombstonedBeforeRemove(t *testing.T) { } } +func TestCityRegistryTransientCityEventProvidersIncludesRegisteredAndPendingCities(t *testing.T) { + t.Setenv("GC_HOME", t.TempDir()) + + registeredPath := writeCityEventLog(t, "registered-only") + pendingPath := writeCityEventLog(t, "pending-city") + runningPath := writeCityEventLog(t, "running-city") + + regFile := supervisor.NewRegistry(supervisor.RegistryPath()) + if err := regFile.Register(registeredPath, "registered-only"); err != nil { + t.Fatal(err) + } + if err := regFile.Register(runningPath, "running-city"); err != nil { + t.Fatal(err) + } + + reg := newCityRegistry() + reg.Add(pendingPath, &managedCity{ + name: "pending-city", + status: "loading_config", + cr: &CityRuntime{cityName: "pending-city"}, + }) + reg.Add(runningPath, &managedCity{ + name: "running-city", + started: true, + cr: &CityRuntime{cityName: "running-city"}, + }) + + providers := reg.TransientCityEventProviders() + t.Cleanup(func() { + for _, p := range providers { + p.Close() //nolint:errcheck + } + }) + + if _, ok := providers["registered-only"]; !ok { + t.Fatalf("registered-only missing from transient providers: %#v", providers) + } + if _, ok := providers["pending-city"]; !ok { + t.Fatalf("pending-city missing from transient providers: %#v", providers) + } + if _, ok := providers["running-city"]; ok { + t.Fatalf("running-city should be handled by running-city multiplexer path, not transient providers") + } + for name, provider := range providers { + if _, ok := provider.(*events.FileRecorder); ok { + t.Fatalf("provider %q should not retain a live file recorder", name) + } + list, err := provider.List(events.Filter{Type: events.CityCreated}) + if err != nil { + t.Fatalf("List(%s): %v", name, err) + } + if len(list) != 1 { + t.Fatalf("List(%s) returned %d city.created events, want 1", name, len(list)) + } + } +} + +func TestCityRegistryTransientCityEventProvidersSkipMissingLogs(t *testing.T) { + t.Setenv("GC_HOME", t.TempDir()) + + missingPath := filepath.Join(t.TempDir(), "missing-city") + if err := os.MkdirAll(missingPath, 0o755); err != nil { + t.Fatal(err) + } + + regFile := supervisor.NewRegistry(supervisor.RegistryPath()) + if err := regFile.Register(missingPath, "missing-city"); err != nil { + t.Fatal(err) + } + + reg := newCityRegistry() + providers := reg.TransientCityEventProviders() + if _, ok := providers["missing-city"]; ok { + t.Fatalf("missing-city should be skipped when events.jsonl is absent: %#v", providers) + } + if _, err := os.Stat(filepath.Join(missingPath, ".gc", "events.jsonl")); !os.IsNotExist(err) { + t.Fatalf("events.jsonl stat = %v, want not exists", err) + } +} + +func writeCityEventLog(t *testing.T, name string) string { + t.Helper() + path := filepath.Join(t.TempDir(), name) + if err := os.MkdirAll(filepath.Join(path, ".gc"), 0o755); err != nil { + t.Fatal(err) + } + rec, err := events.NewFileRecorder(filepath.Join(path, ".gc", "events.jsonl"), io.Discard) + if err != nil { + t.Fatal(err) + } + rec.Record(events.Event{Type: events.CityCreated, Actor: "gc", Subject: name}) + if err := rec.Close(); err != nil { + t.Fatal(err) + } + return path +} + func TestCityRegistrySnapshotImmutability(t *testing.T) { reg := newCityRegistry() cs := &controllerState{} diff --git a/cmd/gc/city_runtime.go b/cmd/gc/city_runtime.go index 403f4ce0d9..828df44b2d 100644 --- a/cmd/gc/city_runtime.go +++ b/cmd/gc/city_runtime.go @@ -271,14 +271,17 @@ func (cr *CityRuntime) run(ctx context.Context) { lastProviderName = v } - cityRoot := filepath.Dir(cr.tomlPath) + cityRoot := cr.cityPath + if cityRoot == "" && cr.tomlPath != "" { + cityRoot = filepath.Dir(cr.tomlPath) + } // Enforce restrictive permissions on .gc/ and its subdirectories. enforceGCPermissions(cr.cityPath, cr.stderr) // Open standalone city bead store when controllerState is unavailable. // When controllerState is present, it manages the cached city store. - if cr.cs == nil { + if cr.cs == nil && cityRoot != "" { if store, err := openCityStoreAt(cityRoot); err != nil { fmt.Fprintf(cr.stderr, "%s: city bead store: %v (auto-suspend disabled)\n", cr.logPrefix, err) //nolint:errcheck // best-effort stderr } else { diff --git a/cmd/gc/city_runtime_test.go b/cmd/gc/city_runtime_test.go index dc731639a1..059bc246e9 100644 --- a/cmd/gc/city_runtime_test.go +++ b/cmd/gc/city_runtime_test.go @@ -1216,6 +1216,7 @@ func TestCityRuntimeBeadReconcileTick_SweepRespectsLiveAssignedWork(t *testing.T } func TestCityRuntimeTick_RefreshesManualSessionOverlayAfterSync(t *testing.T) { + skipSlowCmdGCTest(t, "runs a full runtime tick/reconcile path; run make test-cmd-gc-process for full coverage") cityPath := t.TempDir() if err := os.MkdirAll(filepath.Join(cityPath, "prompts"), 0o755); err != nil { t.Fatalf("mkdir prompts: %v", err) diff --git a/cmd/gc/cityinit_impl.go b/cmd/gc/cityinit_impl.go new file mode 100644 index 0000000000..9a2c8bc0dc --- /dev/null +++ b/cmd/gc/cityinit_impl.go @@ -0,0 +1,552 @@ +package main + +// cityinit.Initializer implementation. Bridges the domain interface +// declared in internal/cityinit to the concrete scaffold + finalize +// helpers in this package. Supplied to api.NewSupervisorMux at +// construction so POST /v0/city calls Init in-process — no +// subprocess, no 30-second deadline, no stderr-scraping. +// +// The long-term plan is to move doInit/finalizeInit and their +// helpers into internal/cityinit so the domain logic physically +// lives in the object model (per engdocs/architecture/api-control-plane.md §1). This +// bridge is the minimum viable unblocker: the HTTP API no longer +// shells out, both surfaces drive the same in-process code path via +// the same typed contract, and the refactor of where the body lives +// is a follow-up. + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "sort" + "strings" + "syscall" + + "github.com/gastownhall/gascity/internal/api" + "github.com/gastownhall/gascity/internal/cityinit" + "github.com/gastownhall/gascity/internal/citylayout" + "github.com/gastownhall/gascity/internal/config" + "github.com/gastownhall/gascity/internal/events" + "github.com/gastownhall/gascity/internal/fsys" + "github.com/gastownhall/gascity/internal/supervisor" +) + +// localInitializer implements cityinit.Initializer by dispatching to +// this package's existing doInit + finalizeInit functions. +type localInitializer struct{} + +// NewInitializer returns the Initializer the supervisor uses to +// service POST /v0/city. Exported so `gc supervisor run` can wire it +// into api.NewSupervisorMux. +func NewInitializer() cityinit.Initializer { + return localInitializer{} +} + +func ensureCityEventLog(cityPath string) { + if fr, err := events.NewFileRecorder(filepath.Join(cityPath, ".gc", "events.jsonl"), io.Discard); err == nil { + fr.Close() //nolint:errcheck // best-effort + } +} + +func recordCityEvent(cityPath, eventType, subject string, payload any) { + fr, err := events.NewFileRecorder(filepath.Join(cityPath, ".gc", "events.jsonl"), io.Discard) + if err != nil { + return + } + defer fr.Close() //nolint:errcheck // best-effort + + raw, err := json.Marshal(payload) + if err != nil { + return + } + fr.Record(events.Event{ + Type: eventType, + Actor: "gc", + Subject: subject, + Payload: raw, + }) +} + +type scaffoldRollbackEntry struct { + mode os.FileMode + data []byte + linkTarget string +} + +type scaffoldSnapshot struct { + root string + entries map[string]scaffoldRollbackEntry +} + +type scaffoldRollbackState struct { + root string + before map[string]scaffoldRollbackEntry + after map[string]scaffoldRollbackEntry +} + +func captureScaffoldSnapshot(root string) (*scaffoldSnapshot, error) { + snapshot := &scaffoldSnapshot{ + root: root, + entries: make(map[string]scaffoldRollbackEntry), + } + for _, rel := range scaffoldManagedPaths() { + if err := snapshot.capture(rel); err != nil { + return nil, err + } + } + return snapshot, nil +} + +func scaffoldManagedPaths() []string { + seen := make(map[string]struct{}, len(initConventionDirs)+5) + paths := make([]string, 0, len(initConventionDirs)+5) + add := func(rel string) { + if rel == "" { + return + } + if _, ok := seen[rel]; ok { + return + } + seen[rel] = struct{}{} + paths = append(paths, rel) + } + + add(citylayout.RuntimeRoot) + add("hooks") + add(citylayout.CityConfigFile) + add("pack.toml") + add(".gitignore") + for _, rel := range initConventionDirs { + add(rel) + } + return paths +} + +func newScaffoldRollbackState(root string) (*scaffoldRollbackState, error) { + snapshot, err := captureScaffoldSnapshot(root) + if err != nil { + return nil, err + } + return &scaffoldRollbackState{ + root: root, + before: snapshot.entries, + }, nil +} + +func (s *scaffoldSnapshot) capture(rel string) error { + abs := filepath.Join(s.root, rel) + _, err := os.Lstat(abs) + if os.IsNotExist(err) { + return nil + } + if err != nil { + return fmt.Errorf("snapshot %q: %w", abs, err) + } + return filepath.Walk(abs, func(path string, info os.FileInfo, walkErr error) error { + if walkErr != nil { + return fmt.Errorf("snapshot %q: %w", path, walkErr) + } + relPath, err := filepath.Rel(s.root, path) + if err != nil { + return fmt.Errorf("relative path for %q: %w", path, err) + } + entry := scaffoldRollbackEntry{mode: info.Mode()} + if info.Mode()&os.ModeSymlink != 0 { + target, err := os.Readlink(path) + if err != nil { + return fmt.Errorf("readlink %q: %w", path, err) + } + entry.linkTarget = target + } else if !info.IsDir() { + data, err := os.ReadFile(path) + if err != nil { + return fmt.Errorf("read %q: %w", path, err) + } + entry.data = data + } + s.entries[filepath.Clean(relPath)] = entry + return nil + }) +} + +func (s *scaffoldRollbackState) markScaffoldState() error { + snapshot, err := captureScaffoldSnapshot(s.root) + if err != nil { + return err + } + s.after = snapshot.entries + return nil +} + +func rollbackEntryEqual(a, b scaffoldRollbackEntry) bool { + return a.mode == b.mode && a.linkTarget == b.linkTarget && bytes.Equal(a.data, b.data) +} + +func restoreRollbackEntry(abs string, entry scaffoldRollbackEntry) error { + switch { + case entry.mode.IsDir(): + return os.MkdirAll(abs, entry.mode.Perm()) + case entry.mode&os.ModeSymlink != 0: + if err := os.MkdirAll(filepath.Dir(abs), 0o755); err != nil { + return err + } + if err := os.Remove(abs); err != nil && !os.IsNotExist(err) { + return err + } + return os.Symlink(entry.linkTarget, abs) + default: + if err := os.MkdirAll(filepath.Dir(abs), 0o755); err != nil { + return err + } + return os.WriteFile(abs, entry.data, entry.mode.Perm()) + } +} + +func (s *scaffoldRollbackState) restore() error { + current, err := captureScaffoldSnapshot(s.root) + if err != nil { + return err + } + + var errs []error + var createdDirs []string + for rel, after := range s.after { + before, hadBefore := s.before[rel] + currentEntry, existsNow := current.entries[rel] + switch { + case !hadBefore: + if after.mode.IsDir() { + createdDirs = append(createdDirs, rel) + continue + } + if existsNow && rollbackEntryEqual(currentEntry, after) { + if err := os.Remove(filepath.Join(s.root, rel)); err != nil && !os.IsNotExist(err) { + errs = append(errs, fmt.Errorf("remove %q: %w", filepath.Join(s.root, rel), err)) + } + } + case rollbackEntryEqual(before, after): + continue + default: + if after.mode.IsDir() { + continue + } + if existsNow && rollbackEntryEqual(currentEntry, after) { + if err := restoreRollbackEntry(filepath.Join(s.root, rel), before); err != nil { + errs = append(errs, fmt.Errorf("restore %q: %w", filepath.Join(s.root, rel), err)) + } + } + } + } + + for rel, before := range s.before { + if _, hadAfter := s.after[rel]; hadAfter { + continue + } + if before.mode.IsDir() { + continue + } + if _, existsNow := current.entries[rel]; existsNow { + continue + } + if err := restoreRollbackEntry(filepath.Join(s.root, rel), before); err != nil { + errs = append(errs, fmt.Errorf("restore %q: %w", filepath.Join(s.root, rel), err)) + } + } + + sort.Slice(createdDirs, func(i, j int) bool { + return len(createdDirs[i]) > len(createdDirs[j]) + }) + for _, rel := range createdDirs { + if err := os.Remove(filepath.Join(s.root, rel)); err != nil && !os.IsNotExist(err) { + if errors.Is(err, syscall.ENOTEMPTY) { + continue + } + errs = append(errs, fmt.Errorf("remove %q: %w", filepath.Join(s.root, rel), err)) + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + return nil +} + +// Scaffold runs the fast portion of city creation so the HTTP API +// handler can return 202 Accepted without blocking on the slow +// finalize work. Writes the on-disk shape (via doInit), then +// registers the city with the supervisor so the reconciler picks +// it up on its next tick. The reconciler owns finalize from there; +// readiness is signaled via city.ready / city.init_failed events on +// the supervisor event bus (see internal/api/event_payloads.go). +func (localInitializer) Scaffold(_ context.Context, req cityinit.InitRequest) (*cityinit.InitResult, error) { + if err := validateInitRequest(&req); err != nil { + return nil, err + } + dir := req.Dir + dirExisted := false + var rollbackState *scaffoldRollbackState + if _, err := os.Stat(dir); err == nil { + dirExisted = true + rollbackState, err = newScaffoldRollbackState(dir) + if err != nil { + return nil, fmt.Errorf("snapshot rollback state for %q: %w", dir, err) + } + } else if !os.IsNotExist(err) { + return nil, fmt.Errorf("stat directory %q: %w", dir, err) + } + if err := os.MkdirAll(dir, 0o755); err != nil { + return nil, fmt.Errorf("creating directory %q: %w", dir, err) + } + + wiz := wizardConfig{ + configName: req.ConfigName, + provider: req.Provider, + startCommand: req.StartCommand, + bootstrapProfile: req.BootstrapProfile, + } + if wiz.configName == "" { + wiz.configName = "tutorial" + } + + if cityHasScaffoldFS(fsys.OSFS{}, dir) { + return nil, cityinit.ErrAlreadyInitialized + } + if code := doInit(fsys.OSFS{}, dir, wiz, req.NameOverride, io.Discard, io.Discard); code != 0 { + if dirExisted && rollbackState != nil { + if markErr := rollbackState.markScaffoldState(); markErr != nil { + return nil, errors.Join(fmt.Errorf("scaffold failed (exit %d)", code), fmt.Errorf("snapshot scaffold state for rollback: %w", markErr)) + } + if cleanupErr := rollbackState.restore(); cleanupErr != nil { + return nil, errors.Join(fmt.Errorf("scaffold failed (exit %d)", code), fmt.Errorf("restoring existing directory after scaffold failure: %w", cleanupErr)) + } + } + if code == initExitAlreadyInitialized { + return nil, cityinit.ErrAlreadyInitialized + } + return nil, fmt.Errorf("scaffold failed (exit %d)", code) + } + + cityName := resolveCityName(req.NameOverride, "", dir) + + // Create .gc/events.jsonl immediately before registration. Two reasons: + // + // 1. The supervisor event multiplexer (see + // internal/api/supervisor.go:buildMultiplexer) includes + // transient-city event providers via + // TransientCityEventSource. With the file in place, a + // subscriber to /v0/events/stream that connects right after + // POST returns 202 sees a non-empty multiplexer and can + // replay events via after_cursor=0. + // + // 2. The supervisor event stream's no-providers precheck rejects + // subscriptions with 503 when the multiplexer is empty. By + // populating at least one event log before registration, + // POST /v0/city → subscribe works even when no other cities + // exist yet (the fresh-supervisor scenario). + // + // The file creation is best-effort. city.created itself is emitted + // only after registration succeeds so synchronous failures do not + // leak a "created" event for a city the supervisor never adopted. + ensureCityEventLog(dir) + if dirExisted && rollbackState != nil { + if err := rollbackState.markScaffoldState(); err != nil { + return nil, fmt.Errorf("snapshot scaffold state for %q: %w", dir, err) + } + } + + // Register the city with the supervisor without blocking on the + // reconciler's tick. The standard registerCityWithSupervisor + // waits for prepareCityForSupervisor to complete, which is the + // very blocking behavior the async POST /v0/city contract + // exists to avoid. + if err := registerCityForAPI(dir, req.NameOverride); err != nil { + if dirExisted { + if rollbackState != nil { + if cleanupErr := rollbackState.restore(); cleanupErr != nil { + return nil, errors.Join(fmt.Errorf("register with supervisor: %w", err), fmt.Errorf("restoring existing directory after failed registration: %w", cleanupErr)) + } + } + } else if cleanupErr := os.RemoveAll(dir); cleanupErr != nil { + return nil, errors.Join(fmt.Errorf("register with supervisor: %w", err), fmt.Errorf("cleaning scaffold after failed registration: %w", cleanupErr)) + } + return nil, fmt.Errorf("register with supervisor: %w", err) + } + recordCityEvent(dir, events.CityCreated, cityName, api.CityCreatedPayload{Name: cityName, Path: dir}) + reloadSupervisorNoWaitHook() + + return &cityinit.InitResult{ + CityName: cityName, + CityPath: dir, + ProviderUsed: req.Provider, + }, nil +} + +// Init scaffolds + finalizes a new city. Errors are mapped to the +// typed sentinels in package cityinit so callers (HTTP API, future +// in-process consumers) can pattern-match via errors.Is. +func (localInitializer) Init(_ context.Context, req cityinit.InitRequest) (*cityinit.InitResult, error) { + if err := validateInitRequest(&req); err != nil { + return nil, err + } + dir := req.Dir + if err := os.MkdirAll(dir, 0o755); err != nil { + return nil, fmt.Errorf("creating directory %q: %w", dir, err) + } + + wiz := wizardConfig{ + configName: req.ConfigName, + provider: req.Provider, + startCommand: req.StartCommand, + bootstrapProfile: req.BootstrapProfile, + } + if wiz.configName == "" { + wiz.configName = "tutorial" + } + + // Check for an already-initialized directory BEFORE calling + // doInit so we can return ErrAlreadyInitialized without also + // writing "gc init: already initialized" to stderr (the CLI + // path wants that; the API does not). + if cityHasScaffoldFS(fsys.OSFS{}, dir) { + return nil, cityinit.ErrAlreadyInitialized + } + + // doInit writes directly to io.Writer arguments (CLI progress + // narration). The API path discards those; error return is + // carried as an exit code, which we translate into typed + // sentinels below. + if code := doInit(fsys.OSFS{}, dir, wiz, req.NameOverride, io.Discard, io.Discard); code != 0 { + if code == initExitAlreadyInitialized { + return nil, cityinit.ErrAlreadyInitialized + } + return nil, fmt.Errorf("scaffold failed (exit %d)", code) + } + + // finalizeInit likewise writes to io.Writer and returns 0/1. + // Discard its narration; the HTTP response conveys structured + // errors via the sentinel types. + if code := finalizeInit(dir, io.Discard, io.Discard, initFinalizeOptions{ + skipProviderReadiness: req.SkipProviderReadiness, + showProgress: false, + commandName: "gc init", + }); code != 0 { + // finalizeInit's current contract is "blocked, check + // stderr". Without a structured return type we can't map + // to specific sentinels; future work splits this out. + return nil, fmt.Errorf("finalize failed (exit %d)", code) + } + + cityName := resolveCityName(req.NameOverride, "", dir) + return &cityinit.InitResult{ + CityName: cityName, + CityPath: dir, + ProviderUsed: req.Provider, + }, nil +} + +// Unregister removes the city's registry entry and signals the +// supervisor to reconcile. Fire-and-forget: returns as soon as the +// registry file is updated and the reload signal is sent. The +// supervisor reconciler discovers the missing entry on its next +// tick, stops the city's controller, and emits city.unregistered +// (or city.unregister_failed on stop failure). See cmd_supervisor.go +// for the reconciler side. +// +// Looks the city up by name. The registry is keyed by path on disk, +// so we scan entries to find the one whose effective name matches. +// Name collisions would violate the registry's uniqueness invariant +// and are rejected at Register time; we take the first match. +// +// Emits city.unregister_requested to the city's event log before +// unregistering so /v0/events/stream subscribers see the start of +// the teardown. Once the registry entry is gone, the transient +// event-provider lookup (cityRegistry.TransientCityEventProviders) +// will still surface this city to new subscribers via its snap.all +// entry until the reconciler fully drops it. +func (localInitializer) Unregister(_ context.Context, req cityinit.UnregisterRequest) (*cityinit.UnregisterResult, error) { + name := strings.TrimSpace(req.CityName) + if name == "" { + return nil, fmt.Errorf("%w: city_name is required", cityinit.ErrNotRegistered) + } + + reg := newSupervisorRegistry() + entries, err := reg.List() + if err != nil { + return nil, fmt.Errorf("reading supervisor registry: %w", err) + } + var match supervisor.CityEntry + var found bool + for _, e := range entries { + if e.EffectiveName() == name { + match = e + found = true + break + } + } + if !found { + return nil, fmt.Errorf("%w: %q", cityinit.ErrNotRegistered, name) + } + + if err := reg.Unregister(match.Path); err != nil { + // Should not happen — we just read this entry — but wrap to + // satisfy the ErrNotRegistered contract if it does. + if errors.Is(err, os.ErrNotExist) { + return nil, fmt.Errorf("%w: %q: %w", cityinit.ErrNotRegistered, name, err) + } + return nil, fmt.Errorf("removing %q from supervisor registry: %w", name, err) + } + recordCityEvent( + match.Path, + events.CityUnregisterRequested, + match.EffectiveName(), + api.CityUnregisterRequestedPayload{Name: match.EffectiveName(), Path: match.Path}, + ) + + // Wake the reconciler; same fire-and-forget signal the Scaffold + // path uses. If the supervisor isn't reachable the periodic + // ticker picks up the change on its next interval. + reloadSupervisorNoWait() + + return &cityinit.UnregisterResult{ + CityName: match.EffectiveName(), + CityPath: match.Path, + }, nil +} + +// validateInitRequest performs the membership / mutual-exclusion +// checks that the HTTP layer previously did inline. Keeping them in +// the bridge means the CLI also benefits from the same validation +// when its call site moves (follow-up). +func validateInitRequest(req *cityinit.InitRequest) error { + if req.Dir == "" { + return fmt.Errorf("%w: dir is required", cityinit.ErrInvalidProvider) + } + if !filepath.IsAbs(req.Dir) { + return fmt.Errorf("dir must be absolute: %q", req.Dir) + } + if req.Provider == "" && req.StartCommand == "" { + return fmt.Errorf("%w: provider or start_command required", cityinit.ErrInvalidProvider) + } + if req.Provider != "" && req.StartCommand != "" { + return fmt.Errorf("%w: provider and start_command are mutually exclusive", cityinit.ErrInvalidProvider) + } + if req.Provider != "" { + if _, ok := config.BuiltinProviders()[req.Provider]; !ok { + return fmt.Errorf("%w: unknown provider %q", cityinit.ErrInvalidProvider, req.Provider) + } + } + if req.BootstrapProfile != "" { + // normalizeBootstrapProfile accepts every spelling the CLI + // and HTTP API currently support; reuse it here so the two + // projections can't disagree. + if _, err := normalizeBootstrapProfile(req.BootstrapProfile); err != nil { + return fmt.Errorf("%w: %w", cityinit.ErrInvalidBootstrapProfile, err) + } + } + return nil +} diff --git a/cmd/gc/cityinit_impl_test.go b/cmd/gc/cityinit_impl_test.go new file mode 100644 index 0000000000..b941ef99c1 --- /dev/null +++ b/cmd/gc/cityinit_impl_test.go @@ -0,0 +1,429 @@ +package main + +import ( + "context" + "encoding/json" + "errors" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/gastownhall/gascity/internal/api" + "github.com/gastownhall/gascity/internal/cityinit" + "github.com/gastownhall/gascity/internal/events" + "github.com/gastownhall/gascity/internal/supervisor" +) + +type fakeSupervisorRegistry struct { + entries []supervisor.CityEntry + listErr error + registerErr error + unregisterErr error +} + +func (f *fakeSupervisorRegistry) List() ([]supervisor.CityEntry, error) { + if f.listErr != nil { + return nil, f.listErr + } + return append([]supervisor.CityEntry(nil), f.entries...), nil +} + +func (f *fakeSupervisorRegistry) Register(string, string) error { + return f.registerErr +} + +func (f *fakeSupervisorRegistry) Unregister(string) error { + return f.unregisterErr +} + +func TestValidateInitRequest(t *testing.T) { + absDir := filepath.Join(t.TempDir(), "city") + tests := []struct { + name string + req cityinit.InitRequest + wantErr error + wantContains string + }{ + { + name: "missing dir", + req: cityinit.InitRequest{Provider: "codex"}, + wantErr: cityinit.ErrInvalidProvider, + }, + { + name: "relative dir", + req: cityinit.InitRequest{Dir: "relative", Provider: "codex"}, + wantContains: "dir must be absolute", + }, + { + name: "missing provider and start command", + req: cityinit.InitRequest{Dir: absDir}, + wantErr: cityinit.ErrInvalidProvider, + }, + { + name: "provider and start command are mutually exclusive", + req: cityinit.InitRequest{Dir: absDir, Provider: "codex", StartCommand: "custom-agent"}, + wantErr: cityinit.ErrInvalidProvider, + wantContains: "mutually exclusive", + }, + { + name: "unknown provider", + req: cityinit.InitRequest{Dir: absDir, Provider: "not-a-provider"}, + wantErr: cityinit.ErrInvalidProvider, + }, + { + name: "bad bootstrap profile", + req: cityinit.InitRequest{Dir: absDir, Provider: "codex", BootstrapProfile: "moon-base"}, + wantErr: cityinit.ErrInvalidBootstrapProfile, + }, + { + name: "builtin provider", + req: cityinit.InitRequest{Dir: absDir, Provider: "codex"}, + wantErr: nil, + }, + { + name: "custom start command", + req: cityinit.InitRequest{Dir: absDir, StartCommand: "custom-agent"}, + wantErr: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := validateInitRequest(&tc.req) + if tc.wantErr == nil { + if tc.wantContains != "" { + if err == nil || !strings.Contains(err.Error(), tc.wantContains) { + t.Fatalf("validateInitRequest() error = %v, want message containing %q", err, tc.wantContains) + } + return + } + if err != nil { + t.Fatalf("validateInitRequest() error = %v, want nil", err) + } + return + } + if !errors.Is(err, tc.wantErr) { + t.Fatalf("validateInitRequest() error = %v, want %v", err, tc.wantErr) + } + if tc.wantContains != "" { + if err == nil || !strings.Contains(err.Error(), tc.wantContains) { + t.Fatalf("validateInitRequest() error = %v, want message containing %q", err, tc.wantContains) + } + } + }) + } +} + +func TestLocalInitializerScaffoldCreatesCityRegistersAndEmitsCreated(t *testing.T) { + t.Setenv("GC_HOME", t.TempDir()) + cityPath := filepath.Join(t.TempDir(), "api-city") + reloadSawCreated := 0 + + oldReloadSupervisorNoWaitHook := reloadSupervisorNoWaitHook + reloadSupervisorNoWaitHook = func() { + evts, err := events.ReadFiltered(filepath.Join(cityPath, ".gc", "events.jsonl"), events.Filter{Type: events.CityCreated}) + if err == nil { + reloadSawCreated = len(evts) + } + } + t.Cleanup(func() { + reloadSupervisorNoWaitHook = oldReloadSupervisorNoWaitHook + }) + + result, err := localInitializer{}.Scaffold(context.Background(), cityinit.InitRequest{ + Dir: cityPath, + Provider: "codex", + BootstrapProfile: bootstrapProfileSingleHostCompat, + NameOverride: "api-city", + }) + if err != nil { + t.Fatalf("Scaffold: %v", err) + } + if result.CityName != "api-city" || result.CityPath != cityPath || result.ProviderUsed != "codex" { + t.Fatalf("Scaffold result = %+v, want api-city/%s/codex", result, cityPath) + } + if _, err := os.Stat(filepath.Join(cityPath, "city.toml")); err != nil { + t.Fatalf("city.toml missing after Scaffold: %v", err) + } + + reg := supervisor.NewRegistry(supervisor.RegistryPath()) + entries, err := reg.List() + if err != nil { + t.Fatal(err) + } + if len(entries) != 1 { + t.Fatalf("registry entries = %+v, want one", entries) + } + if entries[0].EffectiveName() != "api-city" { + t.Fatalf("registry effective name = %q, want api-city", entries[0].EffectiveName()) + } + assertSameTestPath(t, entries[0].Path, cityPath) + + evts, err := events.ReadFiltered(filepath.Join(cityPath, ".gc", "events.jsonl"), events.Filter{Type: events.CityCreated}) + if err != nil { + t.Fatalf("ReadFiltered city.created: %v", err) + } + if len(evts) != 1 { + t.Fatalf("city.created events = %d, want 1: %+v", len(evts), evts) + } + var payload api.CityCreatedPayload + if err := json.Unmarshal(evts[0].Payload, &payload); err != nil { + t.Fatalf("unmarshal city.created payload: %v", err) + } + if payload.Name != "api-city" { + t.Fatalf("payload name = %q, want api-city", payload.Name) + } + assertSameTestPath(t, payload.Path, cityPath) + if reloadSawCreated != 1 { + t.Fatalf("reload saw %d city.created events, want 1 before wake", reloadSawCreated) + } + + _, err = localInitializer{}.Scaffold(context.Background(), cityinit.InitRequest{ + Dir: cityPath, + Provider: "codex", + }) + if !errors.Is(err, cityinit.ErrAlreadyInitialized) { + t.Fatalf("second Scaffold error = %v, want ErrAlreadyInitialized", err) + } +} + +func TestLocalInitializerScaffoldDoesNotEmitCreatedWhenRegisterFails(t *testing.T) { + t.Setenv("GC_HOME", t.TempDir()) + cityPath := filepath.Join(t.TempDir(), "api-city") + + oldNewSupervisorRegistry := newSupervisorRegistry + newSupervisorRegistry = func() supervisorRegistry { + return &fakeSupervisorRegistry{registerErr: errors.New("boom")} + } + t.Cleanup(func() { + newSupervisorRegistry = oldNewSupervisorRegistry + }) + + _, err := localInitializer{}.Scaffold(context.Background(), cityinit.InitRequest{ + Dir: cityPath, + Provider: "codex", + BootstrapProfile: bootstrapProfileSingleHostCompat, + NameOverride: "api-city", + }) + if err == nil || !strings.Contains(err.Error(), "register with supervisor") { + t.Fatalf("Scaffold error = %v, want register with supervisor failure", err) + } + if _, statErr := os.Stat(cityPath); !os.IsNotExist(statErr) { + t.Fatalf("cityPath stat after failed registration = %v, want not exists", statErr) + } + + evts, readErr := events.ReadFiltered(filepath.Join(cityPath, ".gc", "events.jsonl"), events.Filter{Type: events.CityCreated}) + if readErr != nil && !os.IsNotExist(readErr) { + t.Fatalf("ReadFiltered city.created: %v", readErr) + } + if len(evts) != 0 { + t.Fatalf("city.created events = %d, want 0: %+v", len(evts), evts) + } +} + +func TestLocalInitializerScaffoldPreservesExistingDirectoryWhenRegisterFails(t *testing.T) { + t.Setenv("GC_HOME", t.TempDir()) + cityPath := filepath.Join(t.TempDir(), "api-city") + keepPath := filepath.Join(cityPath, "keep.txt") + hooksKeepPath := filepath.Join(cityPath, "hooks", "custom.json") + if err := os.MkdirAll(cityPath, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(keepPath, []byte("keep"), 0o644); err != nil { + t.Fatal(err) + } + if err := os.MkdirAll(filepath.Dir(hooksKeepPath), 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(hooksKeepPath, []byte(`{"custom":true}`), 0o644); err != nil { + t.Fatal(err) + } + + oldNewSupervisorRegistry := newSupervisorRegistry + newSupervisorRegistry = func() supervisorRegistry { + return &fakeSupervisorRegistry{registerErr: errors.New("boom")} + } + t.Cleanup(func() { + newSupervisorRegistry = oldNewSupervisorRegistry + }) + + _, err := localInitializer{}.Scaffold(context.Background(), cityinit.InitRequest{ + Dir: cityPath, + Provider: "codex", + BootstrapProfile: bootstrapProfileSingleHostCompat, + NameOverride: "api-city", + }) + if err == nil || !strings.Contains(err.Error(), "register with supervisor") { + t.Fatalf("Scaffold error = %v, want register with supervisor failure", err) + } + + data, readErr := os.ReadFile(keepPath) + if readErr != nil { + t.Fatalf("ReadFile(%q): %v", keepPath, readErr) + } + if string(data) != "keep" { + t.Fatalf("keep.txt = %q, want keep", string(data)) + } + hooksData, hooksReadErr := os.ReadFile(hooksKeepPath) + if hooksReadErr != nil { + t.Fatalf("ReadFile(%q): %v", hooksKeepPath, hooksReadErr) + } + if string(hooksData) != `{"custom":true}` { + t.Fatalf("custom hook file = %q, want preserved content", string(hooksData)) + } + if _, statErr := os.Stat(filepath.Join(cityPath, "city.toml")); !os.IsNotExist(statErr) { + t.Fatalf("city.toml stat after failed registration = %v, want not exists", statErr) + } + if _, statErr := os.Stat(filepath.Join(cityPath, ".gc")); !os.IsNotExist(statErr) { + t.Fatalf(".gc stat after failed registration = %v, want not exists", statErr) + } + if _, statErr := os.Stat(filepath.Join(cityPath, "hooks", "claude.json")); !os.IsNotExist(statErr) { + t.Fatalf("hooks/claude.json stat after failed registration = %v, want not exists", statErr) + } + + newSupervisorRegistry = oldNewSupervisorRegistry + result, err := localInitializer{}.Scaffold(context.Background(), cityinit.InitRequest{ + Dir: cityPath, + Provider: "codex", + BootstrapProfile: bootstrapProfileSingleHostCompat, + NameOverride: "api-city", + }) + if err != nil { + t.Fatalf("Scaffold retry: %v", err) + } + if result.CityName != "api-city" { + t.Fatalf("Scaffold retry city name = %q, want api-city", result.CityName) + } +} + +func TestLocalInitializerInitScaffoldsAndFinalizes(t *testing.T) { + skipSlowCmdGCTest(t, "runs the full local init scaffold/finalize path; run make test-cmd-gc-process for full coverage") + configureTestDoltIdentityEnv(t) + cityPath := filepath.Join(t.TempDir(), "init-city") + + result, err := localInitializer{}.Init(context.Background(), cityinit.InitRequest{ + Dir: cityPath, + StartCommand: "true", + NameOverride: "init-city", + SkipProviderReadiness: true, + }) + if err != nil { + t.Fatalf("Init: %v", err) + } + if result.CityName != "init-city" || result.CityPath != cityPath { + t.Fatalf("Init result = %+v, want init-city/%s", result, cityPath) + } + if _, err := os.Stat(filepath.Join(cityPath, ".gc")); err != nil { + t.Fatalf(".gc missing after Init finalization: %v", err) + } + + _, err = localInitializer{}.Init(context.Background(), cityinit.InitRequest{ + Dir: cityPath, + StartCommand: "true", + }) + if !errors.Is(err, cityinit.ErrAlreadyInitialized) { + t.Fatalf("second Init error = %v, want ErrAlreadyInitialized", err) + } +} + +func TestLocalInitializerUnregisterRemovesRegistryAndEmitsEvent(t *testing.T) { + t.Setenv("GC_HOME", t.TempDir()) + cityPath := filepath.Join(t.TempDir(), "bright-lights") + if err := os.MkdirAll(filepath.Join(cityPath, ".gc"), 0o755); err != nil { + t.Fatal(err) + } + reg := supervisor.NewRegistry(supervisor.RegistryPath()) + if err := reg.Register(cityPath, "bright-lights"); err != nil { + t.Fatal(err) + } + + result, err := localInitializer{}.Unregister(context.Background(), cityinit.UnregisterRequest{ + CityName: " bright-lights ", + }) + if err != nil { + t.Fatalf("Unregister: %v", err) + } + if result.CityName != "bright-lights" { + t.Fatalf("CityName = %q, want bright-lights", result.CityName) + } + assertSameTestPath(t, result.CityPath, cityPath) + + entries, err := reg.List() + if err != nil { + t.Fatal(err) + } + if len(entries) != 0 { + t.Fatalf("registry entries after unregister = %+v, want empty", entries) + } + + evts, err := events.ReadFiltered(filepath.Join(cityPath, ".gc", "events.jsonl"), events.Filter{Type: events.CityUnregisterRequested}) + if err != nil { + t.Fatalf("ReadFiltered: %v", err) + } + if len(evts) != 1 { + t.Fatalf("unregister_requested events = %d, want 1: %+v", len(evts), evts) + } + if evts[0].Actor != "gc" || evts[0].Subject != "bright-lights" { + t.Fatalf("event actor/subject = %q/%q, want gc/bright-lights", evts[0].Actor, evts[0].Subject) + } + var payload api.CityUnregisterRequestedPayload + if err := json.Unmarshal(evts[0].Payload, &payload); err != nil { + t.Fatalf("unmarshal payload: %v", err) + } + if payload.Name != "bright-lights" { + t.Fatalf("payload name = %q, want bright-lights", payload.Name) + } + assertSameTestPath(t, payload.Path, cityPath) +} + +func TestLocalInitializerUnregisterDoesNotEmitEventWhenRegistryWriteFails(t *testing.T) { + t.Setenv("GC_HOME", t.TempDir()) + cityPath := filepath.Join(t.TempDir(), "bright-lights") + if err := os.MkdirAll(filepath.Join(cityPath, ".gc"), 0o755); err != nil { + t.Fatal(err) + } + + oldNewSupervisorRegistry := newSupervisorRegistry + newSupervisorRegistry = func() supervisorRegistry { + return &fakeSupervisorRegistry{ + entries: []supervisor.CityEntry{{ + Path: cityPath, + Name: "bright-lights", + }}, + unregisterErr: errors.New("boom"), + } + } + t.Cleanup(func() { + newSupervisorRegistry = oldNewSupervisorRegistry + }) + + _, err := localInitializer{}.Unregister(context.Background(), cityinit.UnregisterRequest{ + CityName: "bright-lights", + }) + if err == nil || !strings.Contains(err.Error(), "removing \"bright-lights\" from supervisor registry") { + t.Fatalf("Unregister error = %v, want registry removal failure", err) + } + + evts, readErr := events.ReadFiltered(filepath.Join(cityPath, ".gc", "events.jsonl"), events.Filter{Type: events.CityUnregisterRequested}) + if readErr != nil && !os.IsNotExist(readErr) { + t.Fatalf("ReadFiltered city.unregister_requested: %v", readErr) + } + if len(evts) != 0 { + t.Fatalf("city.unregister_requested events = %d, want 0: %+v", len(evts), evts) + } +} + +func TestLocalInitializerUnregisterMissingCity(t *testing.T) { + t.Setenv("GC_HOME", t.TempDir()) + + _, err := localInitializer{}.Unregister(context.Background(), cityinit.UnregisterRequest{CityName: "missing"}) + if !errors.Is(err, cityinit.ErrNotRegistered) { + t.Fatalf("Unregister missing error = %v, want ErrNotRegistered", err) + } + + _, err = localInitializer{}.Unregister(context.Background(), cityinit.UnregisterRequest{}) + if !errors.Is(err, cityinit.ErrNotRegistered) { + t.Fatalf("Unregister blank error = %v, want ErrNotRegistered", err) + } +} diff --git a/cmd/gc/cmd_beads_city_test.go b/cmd/gc/cmd_beads_city_test.go index 59961e126d..25f865b927 100644 --- a/cmd/gc/cmd_beads_city_test.go +++ b/cmd/gc/cmd_beads_city_test.go @@ -87,6 +87,7 @@ func TestDoBeadsCityEndpointSupportsExecGcBeadsBdProvider(t *testing.T) { } func TestDoBeadsCityUseExternalWritesVerifiedCityAndInheritedRigs(t *testing.T) { + skipSlowCmdGCTest(t, "exercises managed bd provider transition behavior; run make test-cmd-gc-process for full coverage") t.Setenv("GC_BEADS", "bd") cityDir := t.TempDir() @@ -184,6 +185,7 @@ func TestDoBeadsCityUseExternalWritesVerifiedCityAndInheritedRigs(t *testing.T) } func TestDoBeadsCityUseExternalUpdatesIncludedInheritedRigs(t *testing.T) { + skipSlowCmdGCTest(t, "exercises managed bd provider transition behavior; run make test-cmd-gc-process for full coverage") t.Setenv("GC_BEADS", "bd") cityDir := t.TempDir() @@ -414,6 +416,7 @@ func TestDoBeadsCityUseExternalStopFailureKeepsExternalConfig(t *testing.T) { } func TestDoBeadsCityUseExternalRewritesCompatRigWithRelativePath(t *testing.T) { + skipSlowCmdGCTest(t, "exercises managed bd provider transition behavior; run make test-cmd-gc-process for full coverage") t.Setenv("GC_BEADS", "bd") cityDir := t.TempDir() @@ -509,6 +512,7 @@ func TestDoBeadsCityUseExternalPreservesCompatOnlyExplicitRigs(t *testing.T) { } func TestDoBeadsCityUseExternalAdoptUnverifiedSkipsValidation(t *testing.T) { + skipSlowCmdGCTest(t, "exercises managed bd provider transition behavior; run make test-cmd-gc-process for full coverage") t.Setenv("GC_BEADS", "bd") cityDir := t.TempDir() diff --git a/cmd/gc/cmd_dolt_state_test.go b/cmd/gc/cmd_dolt_state_test.go index e5a3a43f45..2b64a257d4 100644 --- a/cmd/gc/cmd_dolt_state_test.go +++ b/cmd/gc/cmd_dolt_state_test.go @@ -328,6 +328,7 @@ func TestDoltStateAllocatePortCmdReusesLiveProviderState(t *testing.T) { } func TestStartTCPListenerProcessInDirRegistersCleanup(t *testing.T) { + skipSlowCmdGCTest(t, "spawns a TCP listener process and verifies cleanup; run make test-cmd-gc-process for full coverage") port := reserveRandomTCPPort(t) dir := t.TempDir() var proc *exec.Cmd @@ -1202,7 +1203,7 @@ func TestDoltStatePreflightCleanCmdRemovesStaleArtifacts(t *testing.T) { } func TestDoltStatePreflightCleanCmdPreservesLiveArtifacts(t *testing.T) { - skipSlowCmdGCTest(t, "spawns managed dolt holder processes; run without -short or via integration packages") + skipSlowCmdGCTest(t, "spawns managed dolt holder processes; run make test-cmd-gc-process for full coverage") if _, err := exec.LookPath("lsof"); err != nil { t.Skip("lsof not installed") } @@ -1249,6 +1250,7 @@ func TestDoltStatePreflightCleanCmdPreservesLiveArtifacts(t *testing.T) { func startTCPListenerProcessInDir(t *testing.T, port int, dir string) *exec.Cmd { t.Helper() + skipSlowCmdGCTest(t, "spawns a TCP listener process to emulate managed dolt; run make test-cmd-gc-process for full coverage") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatalf("MkdirAll(%s): %v", dir, err) } @@ -1297,6 +1299,7 @@ while True: func startLockedDelayedTCPListenerProcessInDir(t *testing.T, lockFile string, port int, dir string, delay time.Duration) *exec.Cmd { t.Helper() + skipSlowCmdGCTest(t, "spawns a delayed TCP listener process to emulate managed dolt recovery; run make test-cmd-gc-process for full coverage") if err := os.MkdirAll(filepath.Dir(lockFile), 0o755); err != nil { t.Fatalf("MkdirAll(%s): %v", filepath.Dir(lockFile), err) } @@ -2131,7 +2134,7 @@ func TestDoltStateStopManagedCmdDoesNotKillImposterPortHolder(t *testing.T) { } func TestDoltStateRecoverManagedCmdReportsReadOnlyAndRestarts(t *testing.T) { - skipSlowCmdGCTest(t, "spawns managed dolt recovery processes; run without -short or via integration packages") + skipSlowCmdGCTest(t, "spawns managed dolt recovery processes; run make test-cmd-gc-process for full coverage") cityPath := t.TempDir() layout, err := resolveManagedDoltRuntimeLayout(cityPath) if err != nil { @@ -2488,7 +2491,7 @@ esac } func TestDoltStateRecoverManagedCmdClearsPublishedStateWhenPreflightCleanupFails(t *testing.T) { - skipSlowCmdGCTest(t, "spawns managed dolt recovery processes; run without -short or via integration packages") + skipSlowCmdGCTest(t, "spawns managed dolt recovery processes; run make test-cmd-gc-process for full coverage") cityPath := t.TempDir() layout, err := resolveManagedDoltRuntimeLayout(cityPath) if err != nil { @@ -2563,7 +2566,7 @@ func TestDoltStateRecoverManagedCmdClearsPublishedStateWhenPreflightCleanupFails } func TestDoltStateRecoverManagedCmdFailsWhenPostStartHealthFails(t *testing.T) { - skipSlowCmdGCTest(t, "spawns managed dolt recovery processes; run without -short or via integration packages") + skipSlowCmdGCTest(t, "spawns managed dolt recovery processes; run make test-cmd-gc-process for full coverage") cityPath := t.TempDir() layout, err := resolveManagedDoltRuntimeLayout(cityPath) if err != nil { diff --git a/cmd/gc/cmd_mail.go b/cmd/gc/cmd_mail.go index d874525b9d..8188c493c7 100644 --- a/cmd/gc/cmd_mail.go +++ b/cmd/gc/cmd_mail.go @@ -529,8 +529,8 @@ func resolveMailTargetsForCommand(identifier string, stderr io.Writer, cmdName s if identifier == "" || identifier == "human" { return resolvedMailTarget{display: "human", recipients: []string{"human"}}, true } - if rawTarget, ok := resolveRawMailTargetForStorelessProvider(identifier, stderr, cmdName); ok { - return rawTarget, true + if isStorelessMailProvider() { + return resolveRawMailTargetForStorelessProvider(identifier, stderr, cmdName) } store, code := openCityStore(stderr, cmdName) if store == nil { diff --git a/cmd/gc/cmd_mail_test.go b/cmd/gc/cmd_mail_test.go index 0855c46b9d..2e51d16a61 100644 --- a/cmd/gc/cmd_mail_test.go +++ b/cmd/gc/cmd_mail_test.go @@ -489,6 +489,11 @@ func TestResolveDefaultMailTargetsForCommand_StorelessProviderUsesFirstCandidate t.Setenv("GC_ALIAS", "codeprobe-worker-1") t.Setenv("GC_SESSION_ID", "codeprobe-worker-gc-1941") t.Setenv("GC_AGENT", "codeprobe-worker") + prev := openMailTargetStore + openMailTargetStore = func() (beads.Store, error) { + return nil, fmt.Errorf("not in a city directory") + } + t.Cleanup(func() { openMailTargetStore = prev }) var stderr bytes.Buffer target, ok := resolveDefaultMailTargetsForCommand(&stderr, "gc mail inbox") @@ -568,6 +573,11 @@ func TestDefaultMailIdentityFallsBackToHumanWithoutAliasSessionOrAgent(t *testin func TestResolveMailAddressForCommand_AllowsStorelessMailProvider(t *testing.T) { t.Setenv("GC_MAIL", "fake") + prev := openMailTargetStore + openMailTargetStore = func() (beads.Store, error) { + return nil, fmt.Errorf("not in a city directory") + } + t.Cleanup(func() { openMailTargetStore = prev }) var stderr bytes.Buffer address, ok := resolveMailAddressForCommand("robot", &stderr, "gc mail inbox") diff --git a/cmd/gc/cmd_rig_endpoint_test.go b/cmd/gc/cmd_rig_endpoint_test.go index c10c0ebcba..a41fb3d8c7 100644 --- a/cmd/gc/cmd_rig_endpoint_test.go +++ b/cmd/gc/cmd_rig_endpoint_test.go @@ -1066,7 +1066,7 @@ func TestCanonicalValidationPasswordUsesCredentialsFileOverride(t *testing.T) { } func TestVerifyExternalDoltEndpointRejectsEmptyExternalDoltDatabase(t *testing.T) { - skipSlowCmdGCTest(t, "requires a managed external dolt endpoint; run without -short or via integration packages") + skipSlowCmdGCTest(t, "requires a managed external dolt endpoint; run make test-cmd-gc-process for full coverage") doltPath, err := exec.LookPath("dolt") if err != nil { t.Skip("dolt not installed") @@ -1167,7 +1167,7 @@ func TestVerifyExternalDoltEndpointRejectsEmptyExternalDoltDatabase(t *testing.T } func TestVerifyExternalDoltEndpointRejectsProjectIdentityMismatch(t *testing.T) { - skipSlowCmdGCTest(t, "requires a managed external dolt endpoint; run without -short or via integration packages") + skipSlowCmdGCTest(t, "requires a managed external dolt endpoint; run make test-cmd-gc-process for full coverage") doltPath, err := exec.LookPath("dolt") if err != nil { t.Skip("dolt not installed") @@ -1271,7 +1271,7 @@ func TestVerifyExternalDoltEndpointRejectsProjectIdentityMismatch(t *testing.T) } func TestVerifyExternalDoltEndpointRejectsMissingLocalProjectID(t *testing.T) { - skipSlowCmdGCTest(t, "requires a managed external dolt endpoint; run without -short or via integration packages") + skipSlowCmdGCTest(t, "requires a managed external dolt endpoint; run make test-cmd-gc-process for full coverage") doltPath, err := exec.LookPath("dolt") if err != nil { t.Skip("dolt not installed") diff --git a/cmd/gc/cmd_session.go b/cmd/gc/cmd_session.go index bf6c7ea6e7..fc49ade40a 100644 --- a/cmd/gc/cmd_session.go +++ b/cmd/gc/cmd_session.go @@ -170,6 +170,7 @@ func cmdSessionNew(args []string, alias, title, titleHint string, noAttach bool, fmt.Fprintf(stderr, "gc session new: %v\n", err) //nolint:errcheck // best-effort stderr return 1 } + sessionTransport := config.ResolveSessionCreateTransport(found.Session, resolved) requestedAlias, err := session.ValidateAlias(alias) if err != nil { fmt.Fprintf(stderr, "gc session new: %v\n", err) //nolint:errcheck // best-effort stderr @@ -192,6 +193,10 @@ func cmdSessionNew(args []string, alias, title, titleHint string, noAttach bool, } sp := newSessionProvider() + if err := validateResolvedSessionTransport(resolved, sessionTransport, sp); err != nil { + fmt.Fprintf(stderr, "gc session new: %v\n", err) //nolint:errcheck // best-effort stderr + return 1 + } // Build the work directory. sessionQualifiedName := workdirutil.SessionQualifiedName(cityPath, found, cfg.Rigs, requestedAlias, explicitName) @@ -223,7 +228,7 @@ func cmdSessionNew(args []string, alias, title, titleHint string, noAttach bool, if err != nil { titleProvider = nil } - sessionCommand, err := resolvedSessionCommand(cityPath, resolved, nil) + sessionCommand, err := resolvedSessionCommand(cityPath, resolved, nil, sessionTransport) if err != nil { fmt.Fprintf(stderr, "gc session new: %v\n", err) //nolint:errcheck // best-effort stderr return 1 @@ -245,6 +250,20 @@ func cmdSessionNew(args []string, alias, title, titleHint string, noAttach bool, if resolved.BuiltinAncestor != "" && resolved.BuiltinAncestor != resolved.Name { kindMeta["builtin_ancestor"] = resolved.BuiltinAncestor } + kindMeta, err = newSessionStoredMCPMetadata( + cityPath, + cfg, + alias, + canonicalTemplate, + resolved.Name, + workDir, + sessionTransport, + kindMeta, + ) + if err != nil { + fmt.Fprintf(stderr, "gc session new: %v\n", err) //nolint:errcheck // best-effort stderr + return 1 + } handle, err := newWorkerSessionHandleForResolvedRuntimeWithConfig( cityPath, store, @@ -257,7 +276,7 @@ func cmdSessionNew(args []string, alias, title, titleHint string, noAttach bool, sessionCommand, found.Provider, workDir, - found.Session, + sessionTransport, resolved, kindMeta, ) @@ -295,8 +314,8 @@ func cmdSessionNew(args []string, alias, title, titleHint string, noAttach bool, fmt.Fprintf(stdout, "Session %s created from template %q (reconciler will start it).\n", info.ID, canonicalTemplate) //nolint:errcheck // best-effort stdout - if !shouldAttachNewSession(noAttach, found.Session) { - if found.Session == "acp" && !noAttach { + if !shouldAttachNewSession(noAttach, sessionTransport) { + if sessionTransport == "acp" && !noAttach { fmt.Fprintln(stdout, "Session uses ACP transport; not attaching.") //nolint:errcheck // best-effort stdout } return 0 @@ -328,6 +347,20 @@ func cmdSessionNew(args []string, alias, title, titleHint string, noAttach bool, if resolved.BuiltinAncestor != "" && resolved.BuiltinAncestor != resolved.Name { kindMeta["builtin_ancestor"] = resolved.BuiltinAncestor } + kindMeta, err = newSessionStoredMCPMetadata( + cityPath, + cfg, + alias, + canonicalTemplate, + resolved.Name, + workDir, + sessionTransport, + kindMeta, + ) + if err != nil { + fmt.Fprintf(stderr, "gc session new: %v\n", err) //nolint:errcheck // best-effort stderr + return 1 + } handle, err := newWorkerSessionHandleForResolvedRuntimeWithConfig( cityPath, store, @@ -340,7 +373,7 @@ func cmdSessionNew(args []string, alias, title, titleHint string, noAttach bool, sessionCommand, found.Provider, workDir, - found.Session, + sessionTransport, resolved, kindMeta, ) @@ -375,8 +408,8 @@ func cmdSessionNew(args []string, alias, title, titleHint string, noAttach bool, fmt.Fprintf(stdout, "Session %s created from template %q.\n", info.ID, canonicalTemplate) //nolint:errcheck // best-effort stdout - if !shouldAttachNewSession(noAttach, found.Session) { - if found.Session == "acp" && !noAttach { + if !shouldAttachNewSession(noAttach, sessionTransport) { + if sessionTransport == "acp" && !noAttach { fmt.Fprintln(stdout, "Session uses ACP transport; not attaching.") //nolint:errcheck // best-effort stdout } return 0 @@ -390,6 +423,35 @@ func cmdSessionNew(args []string, alias, title, titleHint string, noAttach bool, return 0 } +func newSessionStoredMCPMetadata( + cityPath string, + cfg *config.City, + alias, template, provider, workDir, transport string, + metadata map[string]string, +) (map[string]string, error) { + if strings.TrimSpace(transport) != "acp" { + return metadata, nil + } + mcpServers, err := resolvedRuntimeMCPServersWithConfig( + cityPath, + cfg, + alias, + template, + provider, + workDir, + transport, + metadata, + ) + if err != nil { + return nil, err + } + return session.WithStoredMCPMetadata( + metadata, + firstNonEmptyGCString(metadata[session.MCPIdentityMetadataKey], metadata["agent_name"]), + mcpServers, + ) +} + // maybeAutoTitle runs the auto-title flow for a newly created session. // The provider should already be resolved by the caller. It returns a // channel that is closed when background title generation completes. @@ -401,11 +463,52 @@ func maybeAutoTitle(store beads.Store, beadID, userTitle, titleHint string, prov }) } -func resolvedSessionCommand(cityPath string, resolved *config.ResolvedProvider, optionOverrides map[string]string) (string, error) { +type acpRouteRegistrar interface { + RouteACP(name string) +} + +func validateResolvedSessionTransport(resolved *config.ResolvedProvider, transport string, sp runtime.Provider) error { + transport = strings.TrimSpace(transport) + if transport != "acp" { + return nil + } + providerName := "" + if resolved != nil { + providerName = resolved.Name + if !resolved.SupportsACP { + if providerName == "" { + providerName = transport + } + return fmt.Errorf("provider %q does not support ACP transport", providerName) + } + } + if sessionProviderSupportsACP(sp) { + return nil + } + if providerName == "" { + providerName = transport + } + return fmt.Errorf("provider %q requires ACP transport but the session provider cannot route ACP sessions", providerName) +} + +func sessionProviderSupportsACP(sp runtime.Provider) bool { + if sp == nil { + return false + } + if provider, ok := sp.(runtime.TransportCapabilityProvider); ok { + return provider.SupportsTransport("acp") + } + if _, ok := sp.(acpRouteRegistrar); ok { + return true + } + return false +} + +func resolvedSessionCommand(cityPath string, resolved *config.ResolvedProvider, optionOverrides map[string]string, transport string) (string, error) { if resolved == nil { return "", fmt.Errorf("resolved provider is nil") } - launchCommand, err := config.BuildProviderLaunchCommand(cityPath, resolved, optionOverrides) + launchCommand, err := config.BuildProviderLaunchCommand(cityPath, resolved, optionOverrides, transport) if err != nil { return "", fmt.Errorf("resolving provider launch command: %w", err) } diff --git a/cmd/gc/cmd_session_logs_test.go b/cmd/gc/cmd_session_logs_test.go index 29240902a8..0a03159f69 100644 --- a/cmd/gc/cmd_session_logs_test.go +++ b/cmd/gc/cmd_session_logs_test.go @@ -306,6 +306,7 @@ func TestResolveSessionLogPathFallsBackWhenSessionKeyFileMissing(t *testing.T) { } func TestResolveStoredSessionLogSource_UniqueWorkDirFallsBackBeyondLatestAlias(t *testing.T) { + skipSlowCmdGCTest(t, "probes provider transcript lookup before workdir fallback; run make test-cmd-gc-process for full coverage") store := beads.NewMemStore() workDir := t.TempDir() searchBase := t.TempDir() diff --git a/cmd/gc/cmd_session_test.go b/cmd/gc/cmd_session_test.go index 83b950d1a8..e7be2b480f 100644 --- a/cmd/gc/cmd_session_test.go +++ b/cmd/gc/cmd_session_test.go @@ -53,6 +53,24 @@ func (p *attachmentAwareProvider) Respond(_ string, response runtime.Interaction return nil } +type transportCapableSessionProvider struct { + *runtime.Fake +} + +func (p *transportCapableSessionProvider) SupportsTransport(transport string) bool { + return transport == "acp" +} + +type routedRejectingSessionProvider struct { + *runtime.Fake +} + +func (p *routedRejectingSessionProvider) SupportsTransport(string) bool { + return false +} + +func (p *routedRejectingSessionProvider) RouteACP(string) {} + func TestFormatDuration(t *testing.T) { tests := []struct { d time.Duration @@ -372,6 +390,149 @@ func TestCmdSessionNew_PoolTemplateWithoutAliasUsesGeneratedWorkDirIdentity(t *t } } +func TestCmdSessionNew_ACPTemplatePersistsStoredMCPMetadata(t *testing.T) { + t.Setenv("GC_BEADS", "file") + t.Setenv("GC_SESSION", "fake") + + cityDir := t.TempDir() + t.Setenv("GC_CITY", cityDir) + writePoolACPSessionCityTOML(t, cityDir) + writeCatalogFile(t, cityDir, "mcp/identity.template.toml", ` +name = "identity" +command = "/bin/mcp" +args = ["{{.AgentName}}", "{{.WorkDir}}", "{{.TemplateName}}"] +`) + + sockPath := filepath.Join(cityDir, ".gc", "controller.sock") + lis, err := net.Listen("unix", sockPath) + if err != nil { + t.Fatalf("Listen(%q): %v", sockPath, err) + } + defer lis.Close() //nolint:errcheck + + commands := make(chan string, 3) + errCh := make(chan error, 1) + go func() { + defer close(commands) + for i := 0; i < 3; i++ { + conn, err := lis.Accept() + if err != nil { + errCh <- err + return + } + buf := make([]byte, 64) + n, err := conn.Read(buf) + if err != nil { + conn.Close() //nolint:errcheck + errCh <- err + return + } + cmd := string(buf[:n]) + commands <- cmd + reply := "ok\n" + if cmd == "ping\n" { + reply = "123\n" + } + if _, err := conn.Write([]byte(reply)); err != nil { + conn.Close() //nolint:errcheck + errCh <- err + return + } + conn.Close() //nolint:errcheck + } + }() + + var stdout, stderr bytes.Buffer + if code := cmdSessionNew([]string{"demo/ant"}, "", "", "", true, &stdout, &stderr); code != 0 { + t.Fatalf("cmdSessionNew(acp) = %d, want 0; stderr=%s", code, stderr.String()) + } + + gotCommands := make([]string, 0, 3) + deadline := time.After(2 * time.Second) + for len(gotCommands) < 3 { + select { + case err := <-errCh: + if err != nil { + t.Fatalf("controller socket: %v", err) + } + case cmd, ok := <-commands: + if !ok { + if len(gotCommands) != 3 { + t.Fatalf("controller commands = %v, want ping plus 2 pokes", gotCommands) + } + break + } + gotCommands = append(gotCommands, cmd) + case <-deadline: + t.Fatalf("timed out waiting for controller pokes, got %v", gotCommands) + } + } + + bead := onlySessionBead(t, cityDir) + if got := bead.Metadata[session.MCPIdentityMetadataKey]; got == "" { + t.Fatal("mcp_identity metadata = empty, want persisted identity") + } + if got, want := bead.Metadata[session.MCPIdentityMetadataKey], bead.Metadata["agent_name"]; got != want { + t.Fatalf("mcp_identity = %q, want agent_name %q", got, want) + } + if got := bead.Metadata[session.MCPServersSnapshotMetadataKey]; got == "" { + t.Fatal("mcp_servers_snapshot metadata = empty, want persisted snapshot") + } + + servers, err := session.DecodeMCPServersSnapshot(bead.Metadata[session.MCPServersSnapshotMetadataKey]) + if err != nil { + t.Fatalf("DecodeMCPServersSnapshot: %v", err) + } + if len(servers) != 1 { + t.Fatalf("len(snapshot) = %d, want 1", len(servers)) + } + if got, want := servers[0].Args[0], bead.Metadata[session.MCPIdentityMetadataKey]; got != want { + t.Fatalf("snapshot Args[0] = %q, want %q", got, want) + } + if got, want := servers[0].Args[1], bead.Metadata["work_dir"]; got != want { + t.Fatalf("snapshot Args[1] = %q, want %q", got, want) + } + if got, want := servers[0].Args[2], "demo/ant"; got != want { + t.Fatalf("snapshot Args[2] = %q, want %q", got, want) + } +} + +func TestCmdSessionNew_CustomACPProviderDefaultsAgentSessionToACP(t *testing.T) { + t.Setenv("GC_BEADS", "file") + t.Setenv("GC_SESSION", "fake") + + oldBuild := buildSessionProviderByName + t.Cleanup(func() { buildSessionProviderByName = oldBuild }) + buildSessionProviderByName = func(name string, sc config.SessionConfig, cityName, cityPath string) (runtime.Provider, error) { + if name == "acp" { + return &transportCapableSessionProvider{Fake: runtime.NewFake()}, nil + } + return oldBuild(name, sc, cityName, cityPath) + } + + cityDir := t.TempDir() + t.Setenv("GC_CITY", cityDir) + writePoolProviderDefaultACPSessionCityTOML(t, cityDir) + writeCatalogFile(t, cityDir, "mcp/identity.template.toml", ` +name = "identity" +command = "/bin/mcp" +args = ["{{.AgentName}}", "{{.WorkDir}}", "{{.TemplateName}}"] +`) + + var stdout, stderr bytes.Buffer + if code := cmdSessionNew([]string{"demo/ant"}, "", "", "", true, &stdout, &stderr); code != 0 { + t.Fatalf("cmdSessionNew(custom provider acp default) = %d, want 0; stderr=%s", code, stderr.String()) + } + + bead := onlySessionBead(t, cityDir) + if got := bead.Metadata["transport"]; got != "acp" { + t.Fatalf("transport = %q, want %q", got, "acp") + } + if got := bead.Metadata[session.MCPServersSnapshotMetadataKey]; got == "" { + t.Fatal("mcp_servers_snapshot metadata = empty, want persisted snapshot") + } +} + func TestCmdSessionNew_PoolTemplateRejectsAliasMatchingConcreteIdentity(t *testing.T) { t.Setenv("GC_BEADS", "file") t.Setenv("GC_SESSION", "fake") @@ -1121,6 +1282,85 @@ max_active_sessions = 4 } } +func writePoolACPSessionCityTOML(t *testing.T, dir string) { + t.Helper() + if err := os.MkdirAll(filepath.Join(dir, ".gc"), 0o755); err != nil { + t.Fatalf("MkdirAll(.gc): %v", err) + } + rigRoot := filepath.Join(dir, "repos", "demo") + if err := os.MkdirAll(rigRoot, 0o755); err != nil { + t.Fatalf("MkdirAll(rig root): %v", err) + } + data := []byte(fmt.Sprintf(`[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[rigs]] +name = "demo" +path = %q + +[[agent]] +name = "ant" +dir = "demo" +provider = "stub" +session = "acp" +work_dir = ".gc/worktrees/{{.Rig}}/ants/{{.AgentBase}}" +min_active_sessions = 0 +max_active_sessions = 4 + +[providers.stub] +command = "/bin/echo" +path_check = "true" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`, rigRoot)) + if err := os.WriteFile(filepath.Join(dir, "city.toml"), data, 0o644); err != nil { + t.Fatalf("WriteFile(city.toml): %v", err) + } +} + +func writePoolProviderDefaultACPSessionCityTOML(t *testing.T, dir string) { + t.Helper() + if err := os.MkdirAll(filepath.Join(dir, ".gc"), 0o755); err != nil { + t.Fatalf("MkdirAll(.gc): %v", err) + } + rigRoot := filepath.Join(dir, "repos", "demo") + if err := os.MkdirAll(rigRoot, 0o755); err != nil { + t.Fatalf("MkdirAll(rig root): %v", err) + } + data := []byte(fmt.Sprintf(`[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[rigs]] +name = "demo" +path = %q + +[[agent]] +name = "ant" +dir = "demo" +provider = "custom-acp" +work_dir = ".gc/worktrees/{{.Rig}}/ants/{{.AgentBase}}" +min_active_sessions = 0 +max_active_sessions = 4 + +[providers.custom-acp] +command = "/bin/echo" +path_check = "true" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`, rigRoot)) + if err := os.WriteFile(filepath.Join(dir, "city.toml"), data, 0o644); err != nil { + t.Fatalf("WriteFile(city.toml): %v", err) + } +} + func sessionBeads(t *testing.T, cityDir string) []beads.Bead { t.Helper() store, err := openCityStoreAt(cityDir) @@ -1297,7 +1537,7 @@ func TestResolvedSessionCommandIncludesDefaultsAndSettings(t *testing.T) { EffectiveDefaults: config.ComputeEffectiveDefaults(claude.OptionsSchema, claude.OptionDefaults, nil), } - got, err := resolvedSessionCommand(cityPath, resolved, nil) + got, err := resolvedSessionCommand(cityPath, resolved, nil, "") if err != nil { t.Fatalf("resolvedSessionCommand: %v", err) } @@ -1326,7 +1566,7 @@ func TestResolvedSessionCommandAppliesOverridesOverDefaults(t *testing.T) { got, err := resolvedSessionCommand(cityPath, resolved, map[string]string{ "permission_mode": "plan", "effort": "low", - }) + }, "") if err != nil { t.Fatalf("resolvedSessionCommand: %v", err) } @@ -1340,3 +1580,58 @@ func TestResolvedSessionCommandAppliesOverridesOverDefaults(t *testing.T) { t.Fatalf("command %q should include effort=low override", got) } } + +func TestResolvedSessionCommandUsesACPTransportCommand(t *testing.T) { + resolved := &config.ResolvedProvider{ + Name: "opencode", + Command: "/bin/echo", + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + + got, err := resolvedSessionCommand("", resolved, nil, "acp") + if err != nil { + t.Fatalf("resolvedSessionCommand: %v", err) + } + if got != "/bin/echo acp" { + t.Fatalf("command = %q, want %q", got, "/bin/echo acp") + } +} + +func TestValidateResolvedSessionTransportRejectsUnsupportedACPProvider(t *testing.T) { + err := validateResolvedSessionTransport(&config.ResolvedProvider{ + Name: "opencode", + }, "acp", &transportCapableSessionProvider{Fake: runtime.NewFake()}) + if err == nil || !strings.Contains(err.Error(), "does not support ACP transport") { + t.Fatalf("validateResolvedSessionTransport() error = %v, want provider ACP support error", err) + } +} + +func TestValidateResolvedSessionTransportRejectsUnroutableACPProvider(t *testing.T) { + err := validateResolvedSessionTransport(&config.ResolvedProvider{ + Name: "opencode", + SupportsACP: true, + }, "acp", runtime.NewFake()) + if err == nil || !strings.Contains(err.Error(), "requires ACP transport") { + t.Fatalf("validateResolvedSessionTransport() error = %v, want ACP routing error", err) + } +} + +func TestValidateResolvedSessionTransportAcceptsRoutedACPProvider(t *testing.T) { + if err := validateResolvedSessionTransport(&config.ResolvedProvider{ + Name: "opencode", + SupportsACP: true, + }, "acp", &transportCapableSessionProvider{Fake: runtime.NewFake()}); err != nil { + t.Fatalf("validateResolvedSessionTransport() = %v, want nil", err) + } +} + +func TestValidateResolvedSessionTransportRejectsRoutedProviderWhenTransportCapabilityDisablesACP(t *testing.T) { + err := validateResolvedSessionTransport(&config.ResolvedProvider{ + Name: "opencode", + SupportsACP: true, + }, "acp", &routedRejectingSessionProvider{Fake: runtime.NewFake()}) + if err == nil || !strings.Contains(err.Error(), "requires ACP transport") { + t.Fatalf("validateResolvedSessionTransport() error = %v, want ACP routing error", err) + } +} diff --git a/cmd/gc/cmd_sling.go b/cmd/gc/cmd_sling.go index c8ee1bac3e..e737ea0cb7 100644 --- a/cmd/gc/cmd_sling.go +++ b/cmd/gc/cmd_sling.go @@ -103,7 +103,7 @@ Examples: } cmd.Flags().BoolVarP(&formula, "formula", "f", false, "treat argument as formula name") cmd.Flags().BoolVar(&nudge, "nudge", false, "nudge target after routing") - cmd.Flags().BoolVar(&force, "force", false, "suppress warnings and allow cross-rig routing") + cmd.Flags().BoolVar(&force, "force", false, "suppress warnings, allow cross-rig routing, allow graph workflow replacement, and for direct bead routes dispatch even if the bead does not resolve in the local store") cmd.Flags().StringVarP(&title, "title", "t", "", "wisp root bead title (with --formula or --on)") cmd.Flags().StringArrayVar(&vars, "var", nil, "variable substitution for formula (key=value, repeatable)") cmd.Flags().StringVar(&merge, "merge", "", "merge strategy: direct, mr, or local") @@ -195,6 +195,7 @@ func cmdSling(args []string, isFormula, doNudge, force bool, title string, vars } emitLoadCityConfigWarnings(stderr, prov) applyFeatureFlags(cfg) + cityName := loadedCityName(cfg, cityPath) var target, beadOrFormula string switch { @@ -211,7 +212,7 @@ func cmdSling(args []string, isFormula, doNudge, force bool, title string, vars fmt.Fprintf(stderr, "gc sling: --formula requires explicit target\n") //nolint:errcheck // best-effort stderr return 1 } - if !looksLikeBeadID(beadOrFormula) { + if !canInferSlingDefaultTargetFromBead(cfg, beadOrFormula) { fmt.Fprintf(stderr, "gc sling: inline text requires explicit target\n usage: gc sling %q\n", beadOrFormula) //nolint:errcheck // best-effort stderr return 1 } @@ -245,12 +246,10 @@ func cmdSling(args []string, isFormula, doNudge, force bool, title string, vars } sp := newSessionProvider() - cityName := loadedCityName(cfg, cityPath) - storeDir := resolveSlingStoreRoot(cfg, cityPath, beadOrFormula, a) - store, err := openStoreAtForCity(storeDir, cityPath) + storeDir, store, err := openSlingStoreForSource(cfg, cityPath, beadOrFormula, a) if err != nil { - fmt.Fprintf(stderr, "gc sling: opening store %s: %v\n", storeDir, err) //nolint:errcheck // best-effort stderr + fmt.Fprintf(stderr, "gc sling: %v\n", err) //nolint:errcheck // best-effort stderr return 1 } storeRef := workflowStoreRefForDir(storeDir, cityPath, cityName, cfg) @@ -258,17 +257,20 @@ func cmdSling(args []string, isFormula, doNudge, force bool, title string, vars // Inline text mode: if the argument doesn't look like a bead ID // (and we're not in formula mode), create a task bead from the text. - // Skip during dry-run to avoid side effects. - // Also check if the bead exists in the store — hierarchical IDs like - // "Prefix-abc.1" have dots that looksLikeBeadID doesn't recognize. - if !isFormula && !dryRun && !looksLikeBeadID(beadOrFormula) && !beadExistsInStore(store, beadOrFormula) { - created, err := store.Create(beads.Bead{Title: beadOrFormula, Description: stdinDescription, Type: "task"}) - if err != nil { - fmt.Fprintf(stderr, "gc sling: creating bead: %v\n", err) //nolint:errcheck // best-effort stderr - return 1 + // During dry-run, mark the text as preview-only instead of creating it. + inlineText := false + if !isFormula { + createInlineBead, previewInlineText := resolveInlineBeadAction(cfg, beadOrFormula, dryRun) + inlineText = previewInlineText + if createInlineBead { + created, err := store.Create(beads.Bead{Title: beadOrFormula, Description: stdinDescription, Type: "task"}) + if err != nil { + fmt.Fprintf(stderr, "gc sling: creating bead: %v\n", err) //nolint:errcheck // best-effort stderr + return 1 + } + fmt.Fprintf(stdout, "Created %s — %q\n", created.ID, beadOrFormula) //nolint:errcheck // best-effort stdout + beadOrFormula = created.ID } - fmt.Fprintf(stdout, "Created %s — %q\n", created.ID, beadOrFormula) //nolint:errcheck // best-effort stdout - beadOrFormula = created.ID } opts := slingOpts{ @@ -285,6 +287,7 @@ func cmdSling(args []string, isFormula, doNudge, force bool, title string, vars Nudge: doNudge, Force: force, DryRun: dryRun, + InlineText: inlineText, ScopeKind: scopeKind, ScopeRef: scopeRef, } @@ -389,7 +392,10 @@ func resolveSlingStoreRoot(cfg *config.City, cityPath, beadOrFormula string, a c // resolveStoreScopeRoot would silently alias them to the city // scope. Skip them so sling falls back to the agent's rig_dir or // the city store instead of operating on the wrong store. - if bp := beadPrefix(beadOrFormula); bp != "" { + if bp := beadPrefix(beadOrFormula); bp != "" && !looksLikeInlineText(cfg, beadOrFormula) { + if sling.IsHQPrefix(cfg, bp) { + return storeDir + } if rig, found := findRigByPrefix(cfg, bp); found && strings.TrimSpace(rig.Path) != "" { return resolveStoreScopeRoot(cityPath, rig.Path) } @@ -400,6 +406,19 @@ func resolveSlingStoreRoot(cfg *config.City, cityPath, beadOrFormula string, a c return storeDir } +func openSlingStoreForSource(cfg *config.City, cityPath, beadOrFormula string, a config.Agent) (string, beads.Store, error) { + storeDir := resolveSlingStoreRoot(cfg, cityPath, beadOrFormula, a) + store, err := openStoreAtForCity(storeDir, cityPath) + if err != nil { + return "", nil, fmt.Errorf("opening store %s: %w", storeDir, err) + } + return storeDir, store, nil +} + +func canInferSlingDefaultTargetFromBead(cfg *config.City, beadOrFormula string) bool { + return looksLikeBeadID(beadOrFormula) || looksLikeConfiguredBeadID(cfg, beadOrFormula) +} + // populateSlingDepsCallbacks fills in the interface fields on SlingDeps. func populateSlingDepsCallbacks(deps *slingDeps) { deps.Resolver = cliAgentResolver{} @@ -640,6 +659,11 @@ func doSling(opts slingOpts, deps slingDeps, querier BeadQuerier, stdout, stderr printSourceWorkflowConflict(stderr, conflictErr, deps.StoreRef) return 3 } + var missingBeadErr *sling.MissingBeadError + if errors.As(err, &missingBeadErr) { + printMissingBeadError(stderr, missingBeadErr, missingBeadForceApplies(opts)) + return 1 + } fmt.Fprintln(stderr, err) //nolint:errcheck return 1 } @@ -674,8 +698,14 @@ func doSlingBatch(opts slingOpts, deps slingDeps, querier BeadChildQuerier, stdo result, err = sling.DoSlingBatch(opts, deps, querier) } else { result, err = sl.ExpandConvoy(context.Background(), opts.BeadOrFormula, opts.Target, sling.RouteOpts{ - Merge: opts.Merge, NoConvoy: opts.NoConvoy, Owned: opts.Owned, - Nudge: opts.Nudge, Force: opts.Force, SkipPoke: opts.SkipPoke, DryRun: opts.DryRun, + Merge: opts.Merge, + NoConvoy: opts.NoConvoy, + Owned: opts.Owned, + Nudge: opts.Nudge, + Force: opts.Force, + SkipPoke: opts.SkipPoke, + DryRun: opts.DryRun, + InlineText: opts.InlineText, }, querier) } // Print warnings before error check so they're visible on failure. @@ -696,6 +726,11 @@ func doSlingBatch(opts slingOpts, deps slingDeps, querier BeadChildQuerier, stdo if printed := printSourceWorkflowConflicts(stderr, err, deps.StoreRef); printed > 0 { return 3 } + var missingBeadErr *sling.MissingBeadError + if errors.As(err, &missingBeadErr) { + printMissingBeadError(stderr, missingBeadErr, missingBeadForceApplies(opts)) + return 1 + } // In batch mode, per-child FailReasons have already been rendered // by printBatchSlingResult above. The error returned from // DoSlingBatch is an errors.Join of a "N/M children failed" @@ -733,6 +768,19 @@ func doSlingBatch(opts slingOpts, deps slingDeps, querier BeadChildQuerier, stdo return 0 } +func printMissingBeadError(stderr io.Writer, err *sling.MissingBeadError, allowForce bool) { + fmt.Fprintln(stderr, err) //nolint:errcheck + if allowForce { + fmt.Fprintln(stderr, " verify the bead ID, or use --force if it exists in a remote view not yet synced locally") //nolint:errcheck + return + } + fmt.Fprintln(stderr, " verify the bead ID; --force does not bypass missing source validation for formula-backed routes") //nolint:errcheck +} + +func missingBeadForceApplies(opts sling.SlingOpts) bool { + return !opts.IsFormula && opts.OnFormula == "" && (opts.NoFormula || opts.Target.EffectiveDefaultSlingFormula() == "") +} + func sourceWorkflowCleanupCommand(sourceBeadID, storeRef string) string { args := []string{"gc workflow delete-source", sourceBeadID} if storeRef = strings.TrimSpace(storeRef); storeRef != "" { @@ -1673,63 +1721,54 @@ func isCustomSlingQuery(a config.Agent) bool { // one digit to distinguish base36 hashes from English words like // "hello-world". Strings with spaces or multiple dashes (like // "code-review") are treated as inline text for ad-hoc bead creation. -// beadExistsInStore returns true if the given ID resolves to a bead in the store. -// Used as a fallback when looksLikeBeadID returns false for valid hierarchical -// IDs (e.g., "ProjectWrenUnity-0fze.1"). -func beadExistsInStore(store beads.Store, id string) bool { - _, err := store.Get(id) - return err == nil -} - func looksLikeBeadID(s string) bool { - if strings.ContainsAny(s, " \t\n") { + _, baseSuffix, ok := sling.BeadIDParts(s) + if !ok || len(baseSuffix) > 8 { return false } - i := strings.Index(s, "-") - if i <= 0 || i == len(s)-1 { - return false - } - // Must have exactly one dash. - if strings.Count(s, "-") != 1 { - return false + return looksLikeBeadIDSuffix(baseSuffix) +} + +func looksLikeBeadIDSuffix(baseSuffix string) bool { + if len(baseSuffix) <= 4 { + return true } - prefix := s[:i] - for idx, c := range prefix { - if idx == 0 { - if ('A' > c || c > 'Z') && ('a' > c || c > 'z') { - return false - } - continue - } - if ('0' > c || c > '9') && ('a' > c || c > 'z') && ('A' > c || c > 'Z') { - return false + for _, c := range baseSuffix { + if '0' <= c && c <= '9' { + return true } } - suffix := s[i+1:] - // Strip hierarchical child suffix (e.g., ".1" from "0fze.1") for validation. - // Dots delimit parent-child hierarchy in bead IDs. - baseSuffix := suffix - if dot := strings.IndexByte(suffix, '.'); dot > 0 { - baseSuffix = suffix[:dot] + return false +} + +func shouldCreateInlineBead(cfg *config.City, beadOrFormula string) bool { + return looksLikeInlineText(cfg, beadOrFormula) +} + +func resolveInlineBeadAction(cfg *config.City, beadOrFormula string, dryRun bool) (createInlineBead, previewInlineText bool) { + if dryRun && looksLikeInlineText(cfg, beadOrFormula) { + return false, true } - for _, c := range baseSuffix { - if ('0' > c || c > '9') && ('a' > c || c > 'z') && ('A' > c || c > 'Z') { - return false - } + return shouldCreateInlineBead(cfg, beadOrFormula), false +} + +func looksLikeInlineText(cfg *config.City, beadOrFormula string) bool { + return !looksLikeBeadID(beadOrFormula) && !looksLikeConfiguredBeadID(cfg, beadOrFormula) +} + +func looksLikeConfiguredBeadID(cfg *config.City, s string) bool { + prefix, baseSuffix, ok := sling.BeadIDParts(s) + if !ok || len(baseSuffix) > 8 { + return false } - // Bead ID suffixes from bd are base36 hashes (3-8 chars) or - // sequential integers. Short suffixes (1-4 chars) are accepted - // unconditionally — no English words are that short after a dash. - // Longer suffixes (5-8 chars) must contain at least one digit to - // distinguish base36 hashes from English words like "world". - if len(baseSuffix) > 8 { + if cfg == nil { return false } - if len(baseSuffix) <= 4 { + if strings.EqualFold(prefix, config.EffectiveHQPrefix(cfg)) { return true } - for _, c := range baseSuffix { - if '0' <= c && c <= '9' { + for i := range cfg.Rigs { + if strings.EqualFold(prefix, cfg.Rigs[i].EffectivePrefix()) { return true } } diff --git a/cmd/gc/cmd_sling_test.go b/cmd/gc/cmd_sling_test.go index e38667ea7d..80087870b5 100644 --- a/cmd/gc/cmd_sling_test.go +++ b/cmd/gc/cmd_sling_test.go @@ -3,6 +3,7 @@ package main import ( "bytes" "context" + "encoding/json" "errors" "fmt" "io" @@ -43,6 +44,29 @@ func (s *selectiveErrStore) Create(b beads.Bead) (beads.Bead, error) { return s.Store.Create(b) } +type getErrStore struct { + beads.Store + err error +} + +func (s *getErrStore) Get(_ string) (beads.Bead, error) { + return beads.Bead{}, s.err +} + +func seededStore(ids ...string) beads.Store { + seed := make([]beads.Bead, 0, len(ids)) + for _, id := range ids { + seed = append(seed, beads.Bead{ + ID: id, + Title: id, + Type: "task", + Status: "open", + Metadata: map[string]string{}, + }) + } + return beads.NewMemStoreFrom(0, seed, nil) +} + // recordingStore wraps a store and overrides Get for bead injection. type recordingStore struct { beads.Store @@ -90,7 +114,10 @@ func (s *slingTestStore) Get(id string) (beads.Bead, error) { } b, ok := s.synthetic[id] if !ok { - return beads.Bead{}, err + if _, _, looksLikeBead := sling.BeadIDParts(id); !looksLikeBead { + return beads.Bead{}, err + } + return s.ensureSynthetic(id), nil } return b, nil } @@ -953,6 +980,651 @@ dir = "frontend" } } +// setupCmdSlingBeadExistsFixture writes a minimal city.toml with a single +// rig + worker agent and positions the test CWD inside the city. Used by +// the bead-existence tests below. Returns the city directory. +func setupCmdSlingBeadExistsFixture(t *testing.T) string { + t.Helper() + configureIsolatedRuntimeEnv(t) + t.Setenv("GC_BEADS", "file") + + cityDir := t.TempDir() + rigDir := filepath.Join(cityDir, "frontend") + if err := os.MkdirAll(rigDir, 0o755); err != nil { + t.Fatalf("MkdirAll(rig): %v", err) + } + if err := ensureScopedFileStoreLayout(cityDir); err != nil { + t.Fatalf("ensureScopedFileStoreLayout: %v", err) + } + if err := ensurePersistedScopeLocalFileStore(cityDir); err != nil { + t.Fatalf("ensurePersistedScopeLocalFileStore(city): %v", err) + } + if err := ensurePersistedScopeLocalFileStore(rigDir); err != nil { + t.Fatalf("ensurePersistedScopeLocalFileStore(rig): %v", err) + } + cityToml := `[workspace] +name = "demo" + +[[rigs]] +name = "frontend" +path = "frontend" +prefix = "FE" + +[[agent]] +name = "worker" +dir = "frontend" +` + if err := os.WriteFile(filepath.Join(cityDir, "city.toml"), []byte(cityToml), 0o644); err != nil { + t.Fatalf("WriteFile(city.toml): %v", err) + } + t.Chdir(cityDir) + return cityDir +} + +func TestCmdSlingRefusesMissingBead(t *testing.T) { + // A bead-ID-shaped argument that doesn't resolve in the store must + // cause sling to error out — otherwise a fabricated / typo'd ID + // would flow through and strand workers on a dead reference. + setupCmdSlingBeadExistsFixture(t) + + var stdout, stderr bytes.Buffer + code := cmdSling( + []string{"frontend/worker", "FE-ghost1"}, + false, false, false, // isFormula, doNudge, force=false + "", nil, "", + true, false, "", + false, false, false, + "", "", + &stdout, &stderr, + ) + if code == 0 { + t.Fatalf("cmdSling returned 0, want non-zero; stderr: %s", stderr.String()) + } + got := stderr.String() + if !strings.Contains(got, "FE-ghost1") { + t.Errorf("stderr missing bead ID; got: %s", got) + } + if !strings.Contains(got, "not found") { + t.Errorf("stderr missing 'not found' phrasing; got: %s", got) + } + if !strings.Contains(got, "--force") { + t.Errorf("stderr should mention --force as the escape hatch; got: %s", got) + } +} + +func TestPrintMissingBeadErrorFormulaBackedDoesNotSuggestForce(t *testing.T) { + var stderr bytes.Buffer + printMissingBeadError(&stderr, &sling.MissingBeadError{BeadID: "FE-ghost1", StoreRef: "rig:frontend"}, false) + + got := stderr.String() + if strings.Contains(got, "use --force") { + t.Fatalf("stderr = %q, should not suggest force for formula-backed missing source", got) + } + if !strings.Contains(got, "does not bypass missing source validation") { + t.Fatalf("stderr = %q, want formula-backed force diagnostic", got) + } +} + +func TestCmdSlingDryRunRefusesMissingBead(t *testing.T) { + setupCmdSlingBeadExistsFixture(t) + + var stdout, stderr bytes.Buffer + code := cmdSling( + []string{"frontend/worker", "FE-ghost1"}, + false, false, false, + "", nil, "", + true, false, "", + false, false, true, + "", "", + &stdout, &stderr, + ) + if code == 0 { + t.Fatalf("cmdSling dry-run returned 0, want non-zero; stdout=%s stderr=%s", stdout.String(), stderr.String()) + } + got := stderr.String() + if !strings.Contains(got, "FE-ghost1") { + t.Errorf("stderr missing bead ID; got: %s", got) + } + if !strings.Contains(got, "not found") { + t.Errorf("stderr missing missing-bead phrasing; got: %s", got) + } +} + +func TestCmdSlingDryRunPreviewsInlineText(t *testing.T) { + cityDir := setupCmdSlingBeadExistsFixture(t) + + var stdout, stderr bytes.Buffer + code := cmdSling( + []string{"frontend/worker", "write docs"}, + false, false, false, + "", nil, "", + true, false, "", + false, false, true, + "", "", + &stdout, &stderr, + ) + if code != 0 { + t.Fatalf("cmdSling dry-run returned %d, want 0; stdout=%s stderr=%s", code, stdout.String(), stderr.String()) + } + if strings.Contains(stderr.String(), "not found") { + t.Fatalf("stderr = %s, want no missing-bead diagnostic", stderr.String()) + } + out := stdout.String() + if !strings.Contains(out, "write docs") { + t.Fatalf("stdout = %s, want inline text in dry-run preview", out) + } + if strings.Contains(out, "Created ") { + t.Fatalf("stdout = %s, want no bead creation during dry-run", out) + } + if !strings.Contains(out, "No side effects executed (--dry-run).") { + t.Fatalf("stdout = %s, want dry-run footer", out) + } + + rigStore, err := openStoreAtForCity(filepath.Join(cityDir, "frontend"), cityDir) + if err != nil { + t.Fatalf("openStoreAtForCity(rig): %v", err) + } + beadList, err := rigStore.List(beads.ListQuery{AllowScan: true}) + if err != nil { + t.Fatalf("rigStore.List: %v", err) + } + if len(beadList) != 0 { + t.Fatalf("rig store bead count = %d, want 0: %#v", len(beadList), beadList) + } +} + +func TestResolveInlineBeadActionDryRunInlineTextDoesNotProbeStore(t *testing.T) { + create, inlineText := resolveInlineBeadAction(&config.City{}, "write docs", true) + if create { + t.Fatal("create = true, want false during dry-run") + } + if !inlineText { + t.Fatal("inlineText = false, want true") + } +} + +func TestResolveInlineBeadActionWhitespaceInlineTextDoesNotProbeStore(t *testing.T) { + create, inlineText := resolveInlineBeadAction(&config.City{}, "write docs", false) + if !create { + t.Fatal("create = false, want true for whitespace inline text") + } + if inlineText { + t.Fatal("inlineText = true, want false outside dry-run") + } +} + +func TestResolveInlineBeadActionSingleTokenInlineTextDoesNotProbeStore(t *testing.T) { + create, inlineText := resolveInlineBeadAction(&config.City{}, "docs", false) + if !create { + t.Fatal("create = false, want true for single-token inline text") + } + if inlineText { + t.Fatal("inlineText = true, want false outside dry-run") + } +} + +func TestResolveInlineBeadActionBeadIDDoesNotProbeStore(t *testing.T) { + create, inlineText := resolveInlineBeadAction(&config.City{}, "FE-123", false) + if create { + t.Fatal("create = true, want false for bead ID") + } + if inlineText { + t.Fatal("inlineText = true, want false") + } +} + +func TestResolveInlineBeadActionConfiguredAlphaSuffixIsBeadID(t *testing.T) { + cfg := &config.City{ + Workspace: config.Workspace{Name: "test", Prefix: "HQ"}, + Rigs: []config.Rig{{Name: "frontend", Path: "/tmp/frontend", Prefix: "FE"}}, + } + + create, inlineText := resolveInlineBeadAction(cfg, "FE-hello", false) + if create { + t.Fatal("create = true, want false for configured bead ID with all-alpha suffix") + } + if inlineText { + t.Fatal("inlineText = true, want false outside dry-run") + } + + create, inlineText = resolveInlineBeadAction(cfg, "FE-a1pha", false) + if create { + t.Fatal("create = true, want false for configured bead ID with digit") + } + if inlineText { + t.Fatal("inlineText = true, want false for configured bead ID") + } +} + +func TestCmdSlingConfiguredPrefixAllAlphaExistingBeadUsesPrefixStore(t *testing.T) { + configureIsolatedRuntimeEnv(t) + t.Setenv("GC_BEADS", "file") + + cityDir := t.TempDir() + frontendDir := filepath.Join(cityDir, "frontend") + ordersDir := filepath.Join(cityDir, "orders") + for _, dir := range []string{frontendDir, ordersDir} { + if err := os.MkdirAll(dir, 0o755); err != nil { + t.Fatalf("MkdirAll(%s): %v", dir, err) + } + } + if err := ensureScopedFileStoreLayout(cityDir); err != nil { + t.Fatalf("ensureScopedFileStoreLayout: %v", err) + } + for _, dir := range []string{cityDir, frontendDir, ordersDir} { + if err := ensurePersistedScopeLocalFileStore(dir); err != nil { + t.Fatalf("ensurePersistedScopeLocalFileStore(%s): %v", dir, err) + } + } + writeTestFileStoreBeads(t, frontendDir, []beads.Bead{{ + ID: "FE-abcde", + Title: "existing frontend work", + Type: "task", + Status: "open", + Metadata: map[string]string{}, + }}) + cityToml := `[workspace] +name = "demo" + +[[rigs]] +name = "frontend" +path = "frontend" +prefix = "FE" + +[[rigs]] +name = "orders" +path = "orders" +prefix = "OD" + +[[agent]] +name = "worker" +dir = "orders" +` + if err := os.WriteFile(filepath.Join(cityDir, "city.toml"), []byte(cityToml), 0o644); err != nil { + t.Fatalf("WriteFile(city.toml): %v", err) + } + t.Chdir(cityDir) + + var stdout, stderr bytes.Buffer + code := cmdSling( + []string{"orders/worker", "FE-abcde"}, + false, false, true, + "", nil, "", + true, false, "", + true, false, false, + "", "", + &stdout, &stderr, + ) + if code != 0 { + t.Fatalf("cmdSling returned %d, want 0; stdout=%s stderr=%s", code, stdout.String(), stderr.String()) + } + if strings.Contains(stdout.String(), "Created ") { + t.Fatalf("stdout = %q, want existing bead route without inline creation", stdout.String()) + } + + frontendStore, err := openStoreAtForCity(frontendDir, cityDir) + if err != nil { + t.Fatalf("openStoreAtForCity(frontend): %v", err) + } + routed, err := frontendStore.Get("FE-abcde") + if err != nil { + t.Fatalf("frontendStore.Get(FE-abcde): %v", err) + } + if routed.Metadata["gc.routed_to"] != "orders/worker" { + t.Fatalf("frontend bead gc.routed_to = %q, want orders/worker", routed.Metadata["gc.routed_to"]) + } + + ordersStore, err := openStoreAtForCity(ordersDir, cityDir) + if err != nil { + t.Fatalf("openStoreAtForCity(orders): %v", err) + } + ordersBeads, err := ordersStore.List(beads.ListQuery{AllowScan: true}) + if err != nil { + t.Fatalf("ordersStore.List: %v", err) + } + if len(ordersBeads) != 0 { + t.Fatalf("orders store bead count = %d, want 0: %#v", len(ordersBeads), ordersBeads) + } +} + +func TestCmdSlingConfiguredPrefixAllAlphaExistingBeadUsesSelectedPrefixStore(t *testing.T) { + cityDir, frontendDir := setupCmdSlingConfiguredPrefixAllAlphaFrontendFixture(t, false, true) + + var stdout, stderr bytes.Buffer + code := cmdSling( + []string{"frontend/worker", "FE-abcde"}, + false, false, false, + "", nil, "", + true, false, "", + true, false, false, + "", "", + &stdout, &stderr, + ) + if code != 0 { + t.Fatalf("cmdSling returned %d, want 0; stdout=%s stderr=%s", code, stdout.String(), stderr.String()) + } + if strings.Contains(stdout.String(), "Created ") { + t.Fatalf("stdout = %q, want existing bead route without inline creation", stdout.String()) + } + + frontendStore, err := openStoreAtForCity(frontendDir, cityDir) + if err != nil { + t.Fatalf("openStoreAtForCity(frontend): %v", err) + } + routed, err := frontendStore.Get("FE-abcde") + if err != nil { + t.Fatalf("frontendStore.Get(FE-abcde): %v", err) + } + if routed.Metadata["gc.routed_to"] != "frontend/worker" { + t.Fatalf("frontend bead gc.routed_to = %q, want frontend/worker", routed.Metadata["gc.routed_to"]) + } + all, err := frontendStore.List(beads.ListQuery{AllowScan: true}) + if err != nil { + t.Fatalf("frontendStore.List: %v", err) + } + if len(all) != 1 { + t.Fatalf("frontend store bead count = %d, want 1: %#v", len(all), all) + } +} + +func TestCmdSlingOneArgConfiguredPrefixAllAlphaExistingBeadUsesDefaultTarget(t *testing.T) { + cityDir, frontendDir := setupCmdSlingConfiguredPrefixAllAlphaFrontendFixture(t, true, true) + + var stdout, stderr bytes.Buffer + code := cmdSling( + []string{"FE-abcde"}, + false, false, false, + "", nil, "", + true, false, "", + true, false, false, + "", "", + &stdout, &stderr, + ) + if code != 0 { + t.Fatalf("cmdSling returned %d, want 0; stdout=%s stderr=%s", code, stdout.String(), stderr.String()) + } + if strings.Contains(stdout.String(), "Created ") { + t.Fatalf("stdout = %q, want existing bead route without inline creation", stdout.String()) + } + + frontendStore, err := openStoreAtForCity(frontendDir, cityDir) + if err != nil { + t.Fatalf("openStoreAtForCity(frontend): %v", err) + } + routed, err := frontendStore.Get("FE-abcde") + if err != nil { + t.Fatalf("frontendStore.Get(FE-abcde): %v", err) + } + if routed.Metadata["gc.routed_to"] != "frontend/worker" { + t.Fatalf("frontend bead gc.routed_to = %q, want frontend/worker", routed.Metadata["gc.routed_to"]) + } +} + +func setupCmdSlingConfiguredPrefixAllAlphaFrontendFixture(t *testing.T, defaultTarget, seedExisting bool) (cityDir, frontendDir string) { + t.Helper() + configureIsolatedRuntimeEnv(t) + t.Setenv("GC_BEADS", "file") + + cityDir = t.TempDir() + frontendDir = filepath.Join(cityDir, "frontend") + if err := os.MkdirAll(frontendDir, 0o755); err != nil { + t.Fatalf("MkdirAll(frontend): %v", err) + } + if err := ensureScopedFileStoreLayout(cityDir); err != nil { + t.Fatalf("ensureScopedFileStoreLayout: %v", err) + } + for _, dir := range []string{cityDir, frontendDir} { + if err := ensurePersistedScopeLocalFileStore(dir); err != nil { + t.Fatalf("ensurePersistedScopeLocalFileStore(%s): %v", dir, err) + } + } + if seedExisting { + writeTestFileStoreBeads(t, frontendDir, []beads.Bead{{ + ID: "FE-abcde", + Title: "existing frontend work", + Type: "task", + Status: "open", + Metadata: map[string]string{}, + }}) + } + defaultTargetLine := "" + if defaultTarget { + defaultTargetLine = "default_sling_target = \"frontend/worker\"\n" + } + cityToml := `[workspace] +name = "demo" + +[[rigs]] +name = "frontend" +path = "frontend" +prefix = "FE" +` + defaultTargetLine + ` +[[agent]] +name = "worker" +dir = "frontend" +` + if err := os.WriteFile(filepath.Join(cityDir, "city.toml"), []byte(cityToml), 0o644); err != nil { + t.Fatalf("WriteFile(city.toml): %v", err) + } + t.Chdir(cityDir) + return cityDir, frontendDir +} + +func writeTestFileStoreBeads(t *testing.T, scopeRoot string, stored []beads.Bead) { + t.Helper() + data := struct { + Seq int `json:"seq"` + Beads []beads.Bead `json:"beads"` + }{Seq: len(stored), Beads: stored} + raw, err := json.Marshal(data) + if err != nil { + t.Fatalf("Marshal file store beads: %v", err) + } + if err := os.WriteFile(filepath.Join(scopeRoot, ".gc", "beads.json"), append(raw, '\n'), 0o644); err != nil { + t.Fatalf("WriteFile(%s): %v", filepath.Join(scopeRoot, ".gc", "beads.json"), err) + } +} + +func TestCmdSlingForceBypassesMissingBeadCheck(t *testing.T) { + // --force must bypass the bead-existence check. The call may still + // fail further downstream (we don't assert a success exit here), but + // stderr must not contain the "not found" guard message. + setupCmdSlingBeadExistsFixture(t) + + var stdout, stderr bytes.Buffer + _ = cmdSling( + []string{"frontend/worker", "FE-ghost1"}, + false, false, true, // force=true + "", nil, "", + true, false, "", + false, false, false, + "", "", + &stdout, &stderr, + ) + got := stderr.String() + if strings.Contains(got, "not found in store") { + t.Errorf("--force did not bypass bead-existence check; stderr: %s", got) + } +} + +func TestCmdSlingForceMissingBeadPrintsAutoConvoyWarning(t *testing.T) { + configureIsolatedRuntimeEnv(t) + t.Setenv("GC_BEADS", "file") + + cityDir := t.TempDir() + rigDir := filepath.Join(cityDir, "frontend") + if err := os.MkdirAll(rigDir, 0o755); err != nil { + t.Fatalf("MkdirAll(rig): %v", err) + } + if err := ensureScopedFileStoreLayout(cityDir); err != nil { + t.Fatalf("ensureScopedFileStoreLayout: %v", err) + } + for _, dir := range []string{cityDir, rigDir} { + if err := ensurePersistedScopeLocalFileStore(dir); err != nil { + t.Fatalf("ensurePersistedScopeLocalFileStore(%s): %v", dir, err) + } + } + cityToml := `[workspace] +name = "demo" + +[[rigs]] +name = "frontend" +path = "frontend" +prefix = "FE" + +[[agent]] +name = "worker" +dir = "frontend" +sling_query = "true" +` + if err := os.WriteFile(filepath.Join(cityDir, "city.toml"), []byte(cityToml), 0o644); err != nil { + t.Fatalf("WriteFile(city.toml): %v", err) + } + t.Chdir(cityDir) + + var stdout, stderr bytes.Buffer + code := cmdSling( + []string{"frontend/worker", "FE-ghost1"}, + false, false, true, + "", nil, "", + false, false, "", + true, false, false, + "", "", + &stdout, &stderr, + ) + if code != 0 { + t.Fatalf("cmdSling returned %d, want 0; stdout=%s stderr=%s", code, stdout.String(), stderr.String()) + } + if !strings.Contains(stderr.String(), "forced dispatch skipped missing-bead validation") { + t.Fatalf("stderr = %q, want forced missing-bead auto-convoy warning", stderr.String()) + } +} + +func TestCmdSlingAcceptsExistingBead(t *testing.T) { + // When a bead-ID-shaped argument IS present in the store, the new + // existence check must not fire. This test only asserts the check + // does not trip — it doesn't assert sling completes successfully, + // since downstream routing has its own gates (cross-rig, etc.) + // that are out of scope for this change. + cityDir := setupCmdSlingBeadExistsFixture(t) + rigDir := filepath.Join(cityDir, "frontend") + + rigStore, err := openStoreAtForCity(rigDir, cityDir) + if err != nil { + t.Fatalf("openStoreAtForCity(rig): %v", err) + } + seeded, err := rigStore.Create(beads.Bead{Title: "real work", Type: "task"}) + if err != nil { + t.Fatalf("seeding bead: %v", err) + } + + var stdout, stderr bytes.Buffer + _ = cmdSling( + []string{"frontend/worker", seeded.ID}, + false, false, false, // force=false; existence check should pass naturally + "", nil, "", + true, false, "", + false, false, false, + "", "", + &stdout, &stderr, + ) + if strings.Contains(stderr.String(), "not found in store") { + t.Errorf("existence check incorrectly tripped on a real bead; stderr: %s", stderr.String()) + } +} + +func TestCmdSlingRefusesMissingConfiguredFallbackBeadID(t *testing.T) { + configureIsolatedRuntimeEnv(t) + t.Setenv("GC_BEADS", "file") + + cityDir := t.TempDir() + rigDir := filepath.Join(cityDir, "orders") + if err := os.MkdirAll(rigDir, 0o755); err != nil { + t.Fatalf("MkdirAll(rig): %v", err) + } + if err := ensureScopedFileStoreLayout(cityDir); err != nil { + t.Fatalf("ensureScopedFileStoreLayout: %v", err) + } + if err := ensurePersistedScopeLocalFileStore(cityDir); err != nil { + t.Fatalf("ensurePersistedScopeLocalFileStore(city): %v", err) + } + if err := ensurePersistedScopeLocalFileStore(rigDir); err != nil { + t.Fatalf("ensurePersistedScopeLocalFileStore(rig): %v", err) + } + cityToml := `[workspace] +name = "demo" + +[[rigs]] +name = "orders" +path = "orders" +prefix = "od" + +[[agent]] +name = "worker" +dir = "orders" +` + if err := os.WriteFile(filepath.Join(cityDir, "city.toml"), []byte(cityToml), 0o644); err != nil { + t.Fatalf("WriteFile(city.toml): %v", err) + } + t.Chdir(cityDir) + + var stdout, stderr bytes.Buffer + code := cmdSling( + []string{"orders/worker", "od-zzzz1"}, + false, false, false, + "", nil, "", + true, false, "", + false, false, false, + "", "", + &stdout, &stderr, + ) + if code == 0 { + t.Fatalf("cmdSling returned 0, want non-zero; stdout=%s stderr=%s", stdout.String(), stderr.String()) + } + if strings.Contains(stdout.String(), "Created ") { + t.Fatalf("stdout = %q, want missing bead error instead of inline creation", stdout.String()) + } + if !strings.Contains(stderr.String(), "not found") { + t.Fatalf("stderr = %q, want missing bead diagnostic", stderr.String()) + } +} + +func TestCmdSlingRefusesMissingConfiguredPrefixAllAlphaBeadID(t *testing.T) { + cityDir, _ := setupCmdSlingConfiguredPrefixAllAlphaFrontendFixture(t, false, false) + + var stdout, stderr bytes.Buffer + code := cmdSling( + []string{"frontend/worker", "FE-abcde"}, + false, false, false, + "", nil, "", + true, false, "", + true, false, false, + "", "", + &stdout, &stderr, + ) + if code == 0 { + t.Fatalf("cmdSling returned 0, want non-zero; stdout=%s stderr=%s", stdout.String(), stderr.String()) + } + if strings.Contains(stdout.String(), "Created ") { + t.Fatalf("stdout = %q, want missing bead error instead of inline creation", stdout.String()) + } + if !strings.Contains(stderr.String(), "not found") { + t.Fatalf("stderr = %q, want missing bead diagnostic", stderr.String()) + } + + frontendStore, err := openStoreAtForCity(filepath.Join(cityDir, "frontend"), cityDir) + if err != nil { + t.Fatalf("openStoreAtForCity(frontend): %v", err) + } + all, err := frontendStore.List(beads.ListQuery{AllowScan: true}) + if err != nil { + t.Fatalf("frontendStore.List: %v", err) + } + if len(all) != 0 { + t.Fatalf("frontend store bead count = %d, want 0: %#v", len(all), all) + } +} + func TestSlingStoreEnvUsesRigBdRuntimeForMixedProviderRig(t *testing.T) { cityDir := t.TempDir() wantPort := strconv.Itoa(writeReachableManagedDoltState(t, cityDir)) @@ -1473,11 +2145,14 @@ func TestDoSlingBatchGetFails(t *testing.T) { opts := testOpts(a, "BL-42") code := doSlingBatch(opts, deps, q, stdout, stderr) - if code != 0 { - t.Fatalf("doSlingBatch returned %d, want 0 (falls through to doSling); stderr: %s", code, stderr.String()) + if code == 0 { + t.Fatalf("doSlingBatch returned 0, want lookup failure; stdout: %s", stdout.String()) } - if !strings.Contains(stdout.String(), "Slung BL-42") { - t.Errorf("stdout = %q, want direct sling output", stdout.String()) + if !strings.Contains(stderr.String(), "bd not available") { + t.Errorf("stderr = %q, want lookup failure", stderr.String()) + } + if len(runner.calls) != 0 { + t.Fatalf("runner calls = %#v, want none", runner.calls) } } @@ -2180,6 +2855,37 @@ func TestResolveSlingStoreRootPrefersBeadPrefixRig(t *testing.T) { } } +func TestResolveSlingStoreRootUsesPrefixRigForConfiguredAllAlphaBeadID(t *testing.T) { + cityPath := filepath.Join(t.TempDir(), "city") + cfg := &config.City{ + Rigs: []config.Rig{ + {Name: "frontend", Path: filepath.Join("rigs", "frontend"), Prefix: "FE"}, + {Name: "orders", Path: filepath.Join("rigs", "orders"), Prefix: "od"}, + }, + } + + got := resolveSlingStoreRoot(cfg, cityPath, "FE-hello", config.Agent{Dir: "orders"}) + want := filepath.Join(cityPath, "rigs", "frontend") + if got != want { + t.Fatalf("resolveSlingStoreRoot() = %q, want %q", got, want) + } +} + +func TestResolveSlingStoreRootUsesCityRootForHQPrefix(t *testing.T) { + cityPath := filepath.Join(t.TempDir(), "city") + cfg := &config.City{ + Workspace: config.Workspace{Name: "bright-lights", Prefix: "hq"}, + Rigs: []config.Rig{ + {Name: "alpha", Path: filepath.Join("rigs", "alpha"), Prefix: "al"}, + }, + } + + got := resolveSlingStoreRoot(cfg, cityPath, "hq-123", config.Agent{Dir: "alpha"}) + if got != cityPath { + t.Fatalf("resolveSlingStoreRoot() = %q, want city root %q", got, cityPath) + } +} + func TestSlingFormulaRepoDirUsesCanonicalRigRoot(t *testing.T) { cityPath := filepath.Join(t.TempDir(), "city") deps := slingDeps{ @@ -3357,6 +4063,7 @@ func TestDryRunSingleBead(t *testing.T) { q := &fakeQuerier{bead: beads.Bead{ID: "BL-42", Title: "Implement login page", Type: "task", Status: "open"}} deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-42") opts := testOpts(a, "BL-42") opts.DryRun = true code := doSling(opts, deps, q, stdout, stderr) @@ -3408,6 +4115,7 @@ func TestDryRunSingleBeadExpandsSlingQuerySummary(t *testing.T) { q := &fakeQuerier{bead: beads.Bead{ID: "FR-42", Title: "Implement login page", Type: "task", Status: "open"}} deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("FR-42") opts := testOpts(a, "FR-42") opts.DryRun = true code := doSling(opts, deps, q, stdout, stderr) @@ -3464,6 +4172,7 @@ func TestDryRunOnFormula(t *testing.T) { q.childrenOf["BL-42"] = []beads.Bead{} // no molecule children deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-42") opts := testOpts(a, "BL-42") opts.OnFormula = "code-review" opts.DryRun = true @@ -3501,6 +4210,7 @@ func TestDryRunMultiSessionConfig(t *testing.T) { } deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-42") opts := testOpts(a, "BL-42") opts.DryRun = true code := doSling(opts, deps, nil, stdout, stderr) @@ -3637,6 +4347,7 @@ func TestDryRunNudgeRunning(t *testing.T) { a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-1") opts := testOpts(a, "BL-1") opts.Nudge = true opts.DryRun = true @@ -3670,6 +4381,7 @@ func TestDryRunNudgeNotRunning(t *testing.T) { a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-1") opts := testOpts(a, "BL-1") opts.Nudge = true opts.DryRun = true @@ -3691,6 +4403,7 @@ func TestDryRunNoMutations(t *testing.T) { a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-42") opts := testOpts(a, "BL-42") opts.DryRun = true code := doSling(opts, deps, nil, stdout, stderr) @@ -3710,6 +4423,7 @@ func TestDryRunSuspendedWarning(t *testing.T) { a := config.Agent{Name: "mayor", Suspended: true, MaxActiveSessions: intPtr(1)} deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-1") opts := testOpts(a, "BL-1") opts.DryRun = true code := doSling(opts, deps, nil, stdout, stderr) @@ -3740,6 +4454,7 @@ func TestDryRunOnExistingMolecule(t *testing.T) { } deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-42") opts := testOpts(a, "BL-42") opts.OnFormula = "code-review" opts.DryRun = true @@ -3763,6 +4478,7 @@ func TestDryRunNilQuerier(t *testing.T) { a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-42") opts := testOpts(a, "BL-42") opts.DryRun = true code := doSling(opts, deps, nil, stdout, stderr) @@ -4038,6 +4754,7 @@ func TestDryRunIdempotentBead(t *testing.T) { q := &fakeQuerier{bead: beads.Bead{ID: "BL-42", Title: "Login page", Assignee: "mayor", Status: "open"}} deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-42") opts := testOpts(a, "BL-42") opts.DryRun = true code := doSling(opts, deps, q, stdout, stderr) @@ -4275,6 +4992,7 @@ func TestDryRunCrossRigSection(t *testing.T) { q := &fakeQuerier{bead: beads.Bead{ID: "FE-123", Type: "task", Status: "open"}} deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("FE-123") opts := testOpts(a, "FE-123") opts.DryRun = true code := doSling(opts, deps, q, stdout, stderr) @@ -4722,6 +5440,7 @@ func TestDefaultFormulaDryRun(t *testing.T) { a := config.Agent{Name: "polecat", Dir: "hw", DefaultSlingFormula: strPtr("mol-polecat-work")} deps, stdout, stderr := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("HW-42") opts := testOpts(a, "HW-42") opts.DryRun = true code := doSling(opts, deps, nil, stdout, stderr) @@ -5134,7 +5853,7 @@ func TestLooksLikeBeadID(t *testing.T) { } } -func TestBeadExistsInStoreFallback(t *testing.T) { +func TestProbeBeadInStoreFallback(t *testing.T) { base := beads.NewMemStore() store := &recordingStore{ Store: base, @@ -5142,16 +5861,36 @@ func TestBeadExistsInStoreFallback(t *testing.T) { } // beadExistsInStore should find it. - if !beadExistsInStore(store, "ProjectWrenUnity-0fze.1") { + exists, err := sling.ProbeBeadInStore(store, "ProjectWrenUnity-0fze.1") + if err != nil { + t.Fatalf("beadExistsInStore(existing): %v", err) + } + if !exists { t.Error("beadExistsInStore should find existing bead") } // Non-existent bead should return false. - if beadExistsInStore(store, "nonexistent-xyz") { + exists, err = sling.ProbeBeadInStore(store, "nonexistent-xyz") + if err != nil { + t.Fatalf("beadExistsInStore(missing): %v", err) + } + if exists { t.Error("beadExistsInStore should return false for missing bead") } } +func TestProbeBeadInStoreSurfacesLookupError(t *testing.T) { + store := &recordingStore{Store: &getErrStore{Store: beads.NewMemStore(), err: fmt.Errorf("lookup failed")}} + + _, err := sling.ProbeBeadInStore(store, "gc-1") + if err == nil { + t.Fatal("ProbeBeadInStore error = nil, want lookup failure") + } + if !strings.Contains(err.Error(), "lookup failed") { + t.Fatalf("ProbeBeadInStore error = %q, want lookup failure", err) + } +} + func TestOneArgSlingInlineTextRequiresTarget(t *testing.T) { // Inline text with 1 arg should error asking for explicit target. cmd := newSlingCmd(&bytes.Buffer{}, &bytes.Buffer{}) diff --git a/cmd/gc/cmd_start.go b/cmd/gc/cmd_start.go index da1bd15369..189f13fea8 100644 --- a/cmd/gc/cmd_start.go +++ b/cmd/gc/cmd_start.go @@ -981,7 +981,7 @@ func passthroughEnv() map[string]string { } else if home := os.Getenv("HOME"); home != "" { m["XDG_STATE_HOME"] = filepath.Join(home, ".local", "state") } - // Pass through all GC_* and ANTHROPIC_* vars. Agent credentials are + // Pass through GC_* vars and provider credential env. Agent credentials are // included in the global baseline because the SDK cannot know which // agent uses which provider (zero hardcoded roles); the trust boundary // is the managed session itself. @@ -990,7 +990,7 @@ func passthroughEnv() map[string]string { if !ok || val == "" { continue } - if strings.HasPrefix(key, "GC_") || strings.HasPrefix(key, "ANTHROPIC_") { + if strings.HasPrefix(key, "GC_") || isProviderCredentialEnv(key) { m[key] = val } } diff --git a/cmd/gc/cmd_start_test.go b/cmd/gc/cmd_start_test.go index d3e0ee02e3..10a049f53e 100644 --- a/cmd/gc/cmd_start_test.go +++ b/cmd/gc/cmd_start_test.go @@ -211,6 +211,32 @@ func TestPassthroughEnvIncludesClaudeAuthContext(t *testing.T) { } } +func TestPassthroughEnvIncludesProviderCredentialEnv(t *testing.T) { + t.Setenv("ANTHROPIC_API_KEY", "sk-ant-123") + t.Setenv("OPENAI_API_KEY", "sk-openai-123") + t.Setenv("OPENAI_BASE_URL", "https://openai.example.test") + t.Setenv("GEMINI_API_KEY", "gemini-123") + t.Setenv("GOOGLE_API_KEY", "google-123") + t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/tmp/google-credentials.json") + t.Setenv("GOOGLE_CLOUD_PROJECT", "gc-project") + + got := passthroughEnv() + + for key, want := range map[string]string{ + "ANTHROPIC_API_KEY": "sk-ant-123", + "OPENAI_API_KEY": "sk-openai-123", + "OPENAI_BASE_URL": "https://openai.example.test", + "GEMINI_API_KEY": "gemini-123", + "GOOGLE_API_KEY": "google-123", + "GOOGLE_APPLICATION_CREDENTIALS": "/tmp/google-credentials.json", + "GOOGLE_CLOUD_PROJECT": "gc-project", + } { + if got[key] != want { + t.Errorf("passthroughEnv()[%s] = %q, want %q", key, got[key], want) + } + } +} + func TestPassthroughEnvXDGFallbackFromHOME(t *testing.T) { t.Setenv("HOME", "/tmp/gc-home") // Explicitly unset XDG vars so fallback logic fires. diff --git a/cmd/gc/cmd_stop_test.go b/cmd/gc/cmd_stop_test.go index fe6700e060..fc0643cf54 100644 --- a/cmd/gc/cmd_stop_test.go +++ b/cmd/gc/cmd_stop_test.go @@ -126,6 +126,7 @@ func TestCmdStopWaitsForStandaloneControllerExit(t *testing.T) { } func TestStopCityManagedBeadsProviderIfRunningStopsDefaultBD(t *testing.T) { + skipSlowCmdGCTest(t, "exercises managed bd provider shutdown; run make test-cmd-gc-process for full coverage") t.Setenv("GC_BEADS", "bd") cityDir := t.TempDir() diff --git a/cmd/gc/cmd_supervisor.go b/cmd/gc/cmd_supervisor.go index b756ff31c1..b9d762eb4c 100644 --- a/cmd/gc/cmd_supervisor.go +++ b/cmd/gc/cmd_supervisor.go @@ -3,6 +3,7 @@ package main import ( "bufio" "context" + "encoding/json" "errors" "fmt" "io" @@ -698,7 +699,7 @@ func runSupervisor(stdout, stderr io.Writer) int { if readOnly { fmt.Fprintf(stderr, "gc supervisor: binding to %s — mutation endpoints disabled (non-localhost)\n", bind) //nolint:errcheck } - apiMux := api.NewSupervisorMux(registry, readOnly, version, startedAt) + apiMux := api.NewSupervisorMux(registry, NewInitializer(), readOnly, version, startedAt) pprofSrv, pprofErr := api.StartPprof("") if pprofErr != nil { @@ -902,13 +903,13 @@ func reconcileCities( }) for i, mc := range toStop { - name := filepath.Base(toStopPaths[i]) - fmt.Fprintf(stdout, "Unregistered city '%s', stopping...\n", name) //nolint:errcheck - // Reconcile path: stop error is already logged inside stopManagedCity - // and propagating it would require bubbling through the whole - // reconcile loop. The supervisor-shutdown aggregator is the path - // that cares about these errors. - _ = stopManagedCity(mc, toStopPaths[i], stderr) + path := toStopPaths[i] + cityName := mc.name + if cityName == "" { + cityName = filepath.Base(path) + } + fmt.Fprintf(stdout, "Unregistered city '%s', stopping...\n", cityName) //nolint:errcheck + stopErr := stopManagedCity(mc, path, stderr) // Clear backoff so re-registering starts immediately. cr.BatchUpdate(func( _ map[string]*managedCity, @@ -916,10 +917,36 @@ func reconcileCities( initFailures map[string]*initFailRecord, panicHistory map[string]*panicRecord, ) { - delete(panicHistory, toStopPaths[i]) - delete(initFailures, toStopPaths[i]) + delete(panicHistory, path) + delete(initFailures, path) }) - fmt.Fprintf(stdout, "City '%s' stopped.\n", name) //nolint:errcheck + // Emit the terminal unregister event to the city's event log + // so /v0/events/stream subscribers observe completion without + // polling. The event lands on disk BEFORE the running-city + // provider is dropped from the multiplexer, so connected + // subscribers see the event via the running-provider path. + // Best-effort: a failure to open the recorder just means + // subscribers learn via GET /v0/cities instead. + evType := events.CityUnregistered + var payload []byte + if stopErr == nil { + fmt.Fprintf(stdout, "City '%s' stopped.\n", cityName) //nolint:errcheck + p, _ := json.Marshal(api.CityUnregisteredPayload{Name: cityName, Path: path}) + payload = p + } else { + evType = events.CityUnregisterFailed + p, _ := json.Marshal(api.CityUnregisterFailedPayload{Name: cityName, Path: path, Error: stopErr.Error()}) + payload = p + } + if fr, frErr := events.NewFileRecorder(filepath.Join(path, ".gc", "events.jsonl"), stderr); frErr == nil { + fr.Record(events.Event{ + Type: evType, + Actor: "gc", + Subject: cityName, + Payload: payload, + }) + fr.Close() //nolint:errcheck // best-effort + } } // Clear panicHistory and initFailures for any path no longer in the @@ -1132,6 +1159,28 @@ func reconcileCities( ) { delete(initStatus, path) }) + // Emit city.init_failed to the city's event file so + // clients watching /v0/events/stream observe async + // failure signal without polling. Best-effort: if the + // file recorder can't open (e.g. .gc/ missing or + // permissions), fall through to recordInitFailure which + // surfaces the error via /v0/cities. + evPath := filepath.Join(path, ".gc", "events.jsonl") + if fr, frErr := events.NewFileRecorder(evPath, stderr); frErr == nil { + if payload, mErr := json.Marshal(api.CityInitFailedPayload{ + Name: cityName, + Path: path, + Error: err.Error(), + }); mErr == nil { + fr.Record(events.Event{ + Type: events.CityInitFailed, + Actor: "gc", + Subject: cityName, + Payload: payload, + }) + } + fr.Close() //nolint:errcheck // best-effort + } recordInitFailure(cityName, fmt.Sprintf("init: %v", err)) continue } @@ -1161,10 +1210,15 @@ func reconcileCities( var sp runtime.Provider spErr := runPostPrepareStep("creating_session_provider", func() error { - var err error - sp, err = newSessionProviderByName( - effectiveProviderName(cfg.Session.Provider), cfg.Session, cityName, path) - return err + providerName := effectiveProviderName(cfg.Session.Provider) + ctx := sessionProviderContextForCity(cfg, path, providerName) + snapshot := loadProviderSessionSnapshot(ctx) + resolvedSP, err := newSessionProviderFromContextWithError(ctx, snapshot) + if err != nil { + return err + } + sp = resolvedSP + return nil }) if spErr != nil { cr.BatchUpdate(func( @@ -1273,6 +1327,7 @@ func reconcileCities( } cs.ct = cityRuntime.crashTrack() cs.pokeCh = pokeCh + cs.configDirty = configDirty cs.services = cityRuntime.svc cs.startBeadEventWatcher(cityCtx) cityRuntime.setControllerState(cs) @@ -1503,6 +1558,19 @@ func reconcileCities( }(cityName, path, fr, lis, sockPath, sockInfo, lock) rec.Record(events.Event{Type: events.ControllerStarted, Actor: "gc"}) + // Signal city.ready on the supervisor event bus so clients + // that POST /v0/city and subscribe to /v0/events/stream + // observe completion without polling. Handler returned 202 + // synchronously; this event is the async completion signal. + readyPayload, readyErr := json.Marshal(api.CityReadyPayload{Name: cityName, Path: path}) + if readyErr == nil { + rec.Record(events.Event{ + Type: events.CityReady, + Actor: "gc", + Subject: cityName, + Payload: readyPayload, + }) + } telemetry.RecordControllerLifecycle(context.Background(), "started") fmt.Fprintf(stdout, "Launching city '%s' (%s)\n", cityName, path) //nolint:errcheck } diff --git a/cmd/gc/cmd_supervisor_city.go b/cmd/gc/cmd_supervisor_city.go index 61ad8d476a..0be16f7fbf 100644 --- a/cmd/gc/cmd_supervisor_city.go +++ b/cmd/gc/cmd_supervisor_city.go @@ -28,8 +28,19 @@ var ( var ( registerCityWithSupervisorTestHook func(cityPath, commandName string, stdout, stderr io.Writer) (bool, int) supervisorCityErrorHook = supervisorCityError + reloadSupervisorNoWaitHook = reloadSupervisorNoWait ) +type supervisorRegistry interface { + List() ([]supervisor.CityEntry, error) + Register(cityPath, effectiveName string) error + Unregister(cityPath string) error +} + +var newSupervisorRegistry = func() supervisorRegistry { + return supervisor.NewRegistry(supervisor.RegistryPath()) +} + func supervisorCityStartTimeout(cityPath string) time.Duration { timeout := supervisorCityReadyTimeout cfg, err := loadCityConfig(cityPath, io.Discard) @@ -242,6 +253,42 @@ func registerCityWithSupervisorNamed(cityPath, nameOverride string, stdout, stde return 0 } +// registerCityForAPI is the registry-write portion of async +// POST /v0/city. It records the city in the supervisor registry but +// intentionally does NOT wait for readiness. Callers are responsible +// for emitting any lifecycle events they need before waking the +// reconciler, so event ordering stays deterministic. +func registerCityForAPI(cityPath, nameOverride string) error { + cityPath = normalizePathForCompare(cityPath) + name, err := registeredCityName(cityPath, nameOverride) + if err != nil { + return err + } + reg := newSupervisorRegistry() + if err := reg.Register(cityPath, name); err != nil { + return err + } + return nil +} + +// reloadSupervisorNoWait sends a "reload" command to the supervisor +// socket without waiting for the reply. Used by registerCityForAPI +// so the async POST /v0/city handler doesn't block on the +// reconciler tick. +func reloadSupervisorNoWait() { + sockPath, _ := runningSupervisorSocket() + if sockPath == "" { + return + } + conn, err := net.DialTimeout("unix", sockPath, 2*time.Second) + if err != nil { + return + } + defer conn.Close() //nolint:errcheck // best-effort + _ = conn.SetWriteDeadline(time.Now().Add(1 * time.Second)) + _, _ = conn.Write([]byte("reload\n")) +} + func retrySupervisorCityStartAfterControllerLock(cityPath string, stdout, stderr io.Writer, startErr error) (bool, error) { if startErr == nil || !strings.Contains(startErr.Error(), "city failed to start: controller lock: controller already running") { return false, startErr diff --git a/cmd/gc/cmd_supervisor_city_test.go b/cmd/gc/cmd_supervisor_city_test.go index 3abd059bd4..a31e434a44 100644 --- a/cmd/gc/cmd_supervisor_city_test.go +++ b/cmd/gc/cmd_supervisor_city_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "encoding/json" "io" "net" "os" @@ -106,6 +107,35 @@ func TestRegisterCityWithSupervisorKeepsRegistrationWhenCityNeverBecomesReady(t } } +func TestRegisterCityForAPIRegistersWithoutWaitingForReadiness(t *testing.T) { + t.Setenv("GC_HOME", t.TempDir()) + + cityPath := filepath.Join(t.TempDir(), "bright-lights") + if err := os.MkdirAll(cityPath, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(cityPath, "city.toml"), []byte("[workspace]\nname = \"bright-lights\"\n"), 0o644); err != nil { + t.Fatal(err) + } + + if err := registerCityForAPI(cityPath, "api-name"); err != nil { + t.Fatalf("registerCityForAPI: %v", err) + } + + reg := supervisor.NewRegistry(supervisor.RegistryPath()) + entries, err := reg.List() + if err != nil { + t.Fatal(err) + } + if len(entries) != 1 { + t.Fatalf("registry entries = %+v, want one", entries) + } + assertSameTestPath(t, entries[0].Path, cityPath) + if entries[0].EffectiveName() != "api-name" { + t.Fatalf("effective name = %q, want api-name", entries[0].EffectiveName()) + } +} + func TestRegisterCityWithSupervisorRetriesControllerLockInitFailure(t *testing.T) { gcHome := t.TempDir() t.Setenv("GC_HOME", gcHome) @@ -185,7 +215,7 @@ func TestRegisterCityWithSupervisorRetriesControllerLockInitFailure(t *testing.T } func TestRegisterCityWithSupervisorKeepsRegistrationWhenReloadFails(t *testing.T) { - skipSlowCmdGCTest(t, "exercises supervisor registration retry behavior; run without -short for scenario coverage") + skipSlowCmdGCTest(t, "exercises supervisor registration retry behavior; run make test-cmd-gc-process for scenario coverage") gcHome := t.TempDir() t.Setenv("GC_HOME", gcHome) @@ -1108,6 +1138,57 @@ func TestUnregisterCityFromSupervisorReturnsReloadFailureWhenCityDirMissing(t *t } } +func TestReconcileCitiesUnregisterEventUsesManagedCityName(t *testing.T) { + t.Setenv("GC_HOME", t.TempDir()) + + cityPath := filepath.Join(t.TempDir(), "basename-city") + if err := os.MkdirAll(cityPath, 0o755); err != nil { + t.Fatal(err) + } + + done := make(chan struct{}) + close(done) + registry := newCityRegistry() + registry.Add(cityPath, &managedCity{ + name: "effective-city", + started: true, + cancel: func() {}, + done: done, + }) + + reg := supervisor.NewRegistry(supervisor.RegistryPath()) + var stdout, stderr bytes.Buffer + reconcileCities(reg, registry, supervisor.PublicationConfig{}, &stdout, &stderr) + + recorded, err := events.ReadAll(filepath.Join(cityPath, ".gc", "events.jsonl")) + if err != nil { + t.Fatalf("ReadAll(events): %v", err) + } + if len(recorded) != 1 { + t.Fatalf("recorded %d events, want 1", len(recorded)) + } + got := recorded[0] + if got.Type != events.CityUnregistered { + t.Fatalf("event.Type = %q, want %q", got.Type, events.CityUnregistered) + } + if got.Subject != "effective-city" { + t.Fatalf("event.Subject = %q, want effective-city", got.Subject) + } + var payload struct { + Name string `json:"name"` + Path string `json:"path"` + } + if err := json.Unmarshal(got.Payload, &payload); err != nil { + t.Fatalf("json.Unmarshal(payload): %v", err) + } + if payload.Name != "effective-city" { + t.Fatalf("payload.Name = %q, want effective-city", payload.Name) + } + if payload.Path != cityPath { + t.Fatalf("payload.Path = %q, want %q", payload.Path, cityPath) + } +} + func TestUnregisterCityFromSupervisorRestoresRegistrationWhenControllerStopWaitFails(t *testing.T) { gcHome := t.TempDir() t.Setenv("GC_HOME", gcHome) diff --git a/cmd/gc/cmd_supervisor_lifecycle.go b/cmd/gc/cmd_supervisor_lifecycle.go index a4e0c77df9..8ee7a962e7 100644 --- a/cmd/gc/cmd_supervisor_lifecycle.go +++ b/cmd/gc/cmd_supervisor_lifecycle.go @@ -15,6 +15,7 @@ import ( "path/filepath" "regexp" goruntime "runtime" + "sort" "strconv" "strings" "text/template" @@ -42,6 +43,8 @@ var ( } ) +const supervisorServiceFileMode os.FileMode = 0o600 + func newSupervisorRunCmd(stdout, stderr io.Writer) *cobra.Command { return &cobra.Command{ Use: "run", @@ -337,6 +340,12 @@ type supervisorServiceData struct { LaunchdLabel string SafeName string Path string + ExtraEnv []supervisorServiceEnvVar +} + +type supervisorServiceEnvVar struct { + Name string + Value string } func buildSupervisorServiceData() (*supervisorServiceData, error) { @@ -358,6 +367,7 @@ func buildSupervisorServiceData() (*supervisorServiceData, error) { LaunchdLabel: supervisorLaunchdLabel(), SafeName: sanitizeServiceName(filepath.Base(home)), Path: searchpath.ExpandPath(homeDir, goruntime.GOOS, os.Getenv("PATH")), + ExtraEnv: supervisorServiceExtraEnv(), }, nil } @@ -368,6 +378,103 @@ func sanitizeServiceName(name string) string { return strings.Trim(name, "-") } +var supervisorServiceEnvNameRE = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`) + +// Keep persistent service-file env narrow. Provider credentials and user +// context need to survive launchd/systemd startup; arbitrary shell state can +// be opted in with GC_SUPERVISOR_ENV. +var supervisorServiceEnvKeys = map[string]bool{ + "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": true, + "CLAUDE_CODE_EFFORT_LEVEL": true, + "CLAUDE_CODE_OAUTH_TOKEN": true, + "CLAUDE_CODE_SUBAGENT_MODEL": true, + "CLAUDE_CONFIG_DIR": true, + "HOME": true, + "LANG": true, + "LC_ALL": true, + "LC_CTYPE": true, + "LOGNAME": true, + "SHELL": true, + "USER": true, + "XDG_CONFIG_HOME": true, + "XDG_STATE_HOME": true, +} + +var providerCredentialEnvPrefixes = []string{ + "ANTHROPIC_", + "GEMINI_", + "GOOGLE_", + "OPENAI_", +} + +var supervisorServiceFixedEnvKeys = map[string]bool{ + "GC_HOME": true, + "PATH": true, + "XDG_RUNTIME_DIR": true, +} + +func supervisorServiceExtraEnv() []supervisorServiceEnvVar { + env := make(map[string]string) + for _, entry := range os.Environ() { + key, val, ok := strings.Cut(entry, "=") + if !ok || val == "" || !shouldPersistSupervisorEnv(key) { + continue + } + env[key] = val + } + for _, key := range supervisorServiceExplicitEnvKeys(os.Getenv("GC_SUPERVISOR_ENV")) { + if val := os.Getenv(key); val != "" { + env[key] = val + } + } + + keys := make([]string, 0, len(env)) + for key := range env { + keys = append(keys, key) + } + sort.Strings(keys) + out := make([]supervisorServiceEnvVar, 0, len(keys)) + for _, key := range keys { + out = append(out, supervisorServiceEnvVar{Name: key, Value: env[key]}) + } + return out +} + +func shouldPersistSupervisorEnv(key string) bool { + if !supervisorServiceEnvNameRE.MatchString(key) || supervisorServiceFixedEnvKeys[key] { + return false + } + if supervisorServiceEnvKeys[key] { + return true + } + return isProviderCredentialEnv(key) +} + +func isProviderCredentialEnv(key string) bool { + for _, prefix := range providerCredentialEnvPrefixes { + if strings.HasPrefix(key, prefix) { + return true + } + } + return false +} + +func supervisorServiceExplicitEnvKeys(raw string) []string { + fields := strings.Fields(strings.NewReplacer(",", " ", ";", " ").Replace(raw)) + out := make([]string, 0, len(fields)) + seen := make(map[string]bool, len(fields)) + for _, field := range fields { + key := strings.TrimSpace(field) + if key == "" || seen[key] || !supervisorServiceEnvNameRE.MatchString(key) || supervisorServiceFixedEnvKeys[key] { + continue + } + seen[key] = true + out = append(out, key) + } + sort.Strings(out) + return out +} + const ( defaultSupervisorLaunchdLabel = "com.gascity.supervisor" defaultSupervisorSystemdUnit = "gascity-supervisor.service" @@ -436,6 +543,10 @@ const supervisorLaunchdTemplate = ` {{end}} PATH {{xmlesc .Path}} + {{range .ExtraEnv}} + {{xmlesc .Name}} + {{xmlesc .Value}} + {{end}} @@ -454,6 +565,8 @@ StandardError=append:{{.LogPath}} Environment=GC_HOME="{{.GCHome}}" {{if .XDGRuntimeDir}}Environment=XDG_RUNTIME_DIR="{{.XDGRuntimeDir}}" {{end}}Environment=PATH="{{.Path}}" +{{range .ExtraEnv}}Environment={{systemdenv .Name .Value}} +{{end}} [Install] WantedBy=default.target @@ -464,8 +577,12 @@ func xmlEscape(s string) string { return r.Replace(s) } +func systemdEnv(name, value string) string { + return name + "=" + strconv.Quote(value) +} + func renderSupervisorTemplate(tmplStr string, data *supervisorServiceData) (string, error) { - funcMap := template.FuncMap{"xmlesc": xmlEscape} + funcMap := template.FuncMap{"xmlesc": xmlEscape, "systemdenv": systemdEnv} tmpl, err := template.New("service").Funcs(funcMap).Parse(tmplStr) if err != nil { return "", err @@ -477,6 +594,20 @@ func renderSupervisorTemplate(tmplStr string, data *supervisorServiceData) (stri return buf.String(), nil } +func writeSupervisorServiceFile(path string, content []byte) error { + if _, err := os.Stat(path); err == nil { + if err := os.Chmod(path, supervisorServiceFileMode); err != nil { + return err + } + } else if !os.IsNotExist(err) { + return err + } + if err := os.WriteFile(path, content, supervisorServiceFileMode); err != nil { + return err + } + return os.Chmod(path, supervisorServiceFileMode) +} + func supervisorLaunchdPlistPath() string { home, _ := os.UserHomeDir() return filepath.Join(home, "Library", "LaunchAgents", supervisorLaunchdLabel()+".plist") @@ -694,7 +825,7 @@ func rollbackNewSupervisorLaunchdInstall(path string, restoreLegacy bool) error func restorePreviousSupervisorLaunchdInstall(path string, previousContent []byte) error { var errs []error _ = supervisorLaunchctlRun("unload", path) - if err := os.WriteFile(path, previousContent, 0o644); err != nil { + if err := writeSupervisorServiceFile(path, previousContent); err != nil { errs = append(errs, fmt.Errorf("restoring previous plist %s: %w", path, err)) } else if err := supervisorLaunchctlRun("load", path); err != nil { errs = append(errs, fmt.Errorf("reloading previous plist %s: %w", path, err)) @@ -725,7 +856,7 @@ func restorePreviousSupervisorSystemdInstall(path, service string, previousConte if restart { _ = supervisorSystemctlRun("--user", "stop", service) } - if err := os.WriteFile(path, previousContent, 0o644); err != nil { + if err := writeSupervisorServiceFile(path, previousContent); err != nil { errs = append(errs, fmt.Errorf("restoring previous unit %s: %w", path, err)) return errors.Join(errs...) } @@ -762,7 +893,7 @@ func installSupervisorLaunchd(data *supervisorServiceData, stdout, stderr io.Wri fmt.Fprintf(stderr, "gc supervisor install: %v\n", err) //nolint:errcheck // best-effort stderr return 1 } - if err := os.WriteFile(path, []byte(content), 0o644); err != nil { + if err := writeSupervisorServiceFile(path, []byte(content)); err != nil { fmt.Fprintf(stderr, "gc supervisor install: writing plist: %v\n", err) //nolint:errcheck // best-effort stderr return 1 } @@ -829,7 +960,7 @@ func installSupervisorSystemd(data *supervisorServiceData, stdout, stderr io.Wri return 1 } contentChanged := string(existing) != content - if err := os.WriteFile(path, []byte(content), 0o644); err != nil { + if err := writeSupervisorServiceFile(path, []byte(content)); err != nil { fmt.Fprintf(stderr, "gc supervisor install: writing unit: %v\n", err) //nolint:errcheck // best-effort stderr return 1 } diff --git a/cmd/gc/cmd_supervisor_test.go b/cmd/gc/cmd_supervisor_test.go index 4df9fd192f..51e5731991 100644 --- a/cmd/gc/cmd_supervisor_test.go +++ b/cmd/gc/cmd_supervisor_test.go @@ -203,6 +203,10 @@ func TestRenderSupervisorLaunchdTemplate(t *testing.T) { XDGRuntimeDir: "/tmp/gc-run", LaunchdLabel: defaultSupervisorLaunchdLabel, Path: "/usr/local/bin:/usr/bin:/bin", + ExtraEnv: []supervisorServiceEnvVar{ + {Name: "ANTHROPIC_API_KEY", Value: `sk-&<"'>`}, + {Name: "OPENAI_API_KEY", Value: "sk-openai-123"}, + }, } content, err := renderSupervisorTemplate(supervisorLaunchdTemplate, data) @@ -220,6 +224,10 @@ func TestRenderSupervisorLaunchdTemplate(t *testing.T) { "XDG_RUNTIME_DIR", "/tmp/gc-run", "PATH", + "ANTHROPIC_API_KEY", + "sk-&<"'>", + "OPENAI_API_KEY", + "sk-openai-123", } { if !strings.Contains(content, check) { t.Fatalf("launchd template missing %q", check) @@ -235,6 +243,10 @@ func TestRenderSupervisorSystemdTemplate(t *testing.T) { XDGRuntimeDir: "/tmp/gc-run", LaunchdLabel: defaultSupervisorLaunchdLabel, Path: "/usr/local/bin:/usr/bin:/bin", + ExtraEnv: []supervisorServiceEnvVar{ + {Name: "ANTHROPIC_API_KEY", Value: `sk-"ant"\value`}, + {Name: "OPENAI_API_KEY", Value: "sk-openai-123"}, + }, } content, err := renderSupervisorTemplate(supervisorSystemdTemplate, data) @@ -249,6 +261,8 @@ func TestRenderSupervisorSystemdTemplate(t *testing.T) { `Environment=GC_HOME="/home/user/.gc"`, `Environment=XDG_RUNTIME_DIR="/tmp/gc-run"`, `Environment=PATH="/usr/local/bin:/usr/bin:/bin"`, + `Environment=ANTHROPIC_API_KEY="sk-\"ant\"\\value"`, + `Environment=OPENAI_API_KEY="sk-openai-123"`, } { if !strings.Contains(content, check) { t.Fatalf("systemd template missing %q", check) @@ -256,6 +270,57 @@ func TestRenderSupervisorSystemdTemplate(t *testing.T) { } } +func TestBuildSupervisorServiceDataIncludesProviderEnv(t *testing.T) { + homeDir := t.TempDir() + t.Setenv("HOME", homeDir) + t.Setenv("GC_HOME", filepath.Join(homeDir, ".gc")) + t.Setenv("PATH", "/usr/local/bin:/usr/bin:/bin") + t.Setenv("XDG_RUNTIME_DIR", "/tmp/gc-run") + t.Setenv("ANTHROPIC_API_KEY", "sk-ant-123") + t.Setenv("ANTHROPIC_BASE_URL", "https://anthropic.example.test") + t.Setenv("OPENAI_API_KEY", "sk-openai-123") + t.Setenv("GEMINI_API_KEY", "gemini-123") + t.Setenv("GOOGLE_CLOUD_PROJECT", "gc-project") + t.Setenv("CLAUDE_CONFIG_DIR", filepath.Join(homeDir, ".claude")) + t.Setenv("GC_SUPERVISOR_ENV", "CUSTOM_PROVIDER_TOKEN,IGNORED_EMPTY") + t.Setenv("CUSTOM_PROVIDER_TOKEN", "custom-token") + t.Setenv("IGNORED_EMPTY", "") + t.Setenv("UNRELATED_SECRET", "do-not-persist") + + data, err := buildSupervisorServiceData() + if err != nil { + t.Fatalf("buildSupervisorServiceData: %v", err) + } + + got := supervisorServiceEnvMap(data.ExtraEnv) + for key, want := range map[string]string{ + "ANTHROPIC_API_KEY": "sk-ant-123", + "ANTHROPIC_BASE_URL": "https://anthropic.example.test", + "OPENAI_API_KEY": "sk-openai-123", + "GEMINI_API_KEY": "gemini-123", + "GOOGLE_CLOUD_PROJECT": "gc-project", + "CLAUDE_CONFIG_DIR": filepath.Join(homeDir, ".claude"), + "CUSTOM_PROVIDER_TOKEN": "custom-token", + } { + if got[key] != want { + t.Fatalf("ExtraEnv[%s] = %q, want %q (all env: %#v)", key, got[key], want, got) + } + } + for _, key := range []string{"GC_HOME", "PATH", "XDG_RUNTIME_DIR", "IGNORED_EMPTY", "UNRELATED_SECRET"} { + if _, ok := got[key]; ok { + t.Fatalf("ExtraEnv should not include %s: %#v", key, got) + } + } +} + +func supervisorServiceEnvMap(vars []supervisorServiceEnvVar) map[string]string { + m := make(map[string]string, len(vars)) + for _, item := range vars { + m[item.Name] = item.Value + } + return m +} + func TestBuildSupervisorServiceDataExpandsUserManagedPath(t *testing.T) { homeDir := t.TempDir() nvmBin := filepath.Join(homeDir, ".nvm", "versions", "node", "v22.14.0", "bin") @@ -572,6 +637,57 @@ func TestInstallSupervisorSystemdRestartsWhenUnitChangesAndServiceActive(t *test if strings.Contains(joined, "--user start gascity-supervisor.service") { t.Fatalf("systemctl calls = %v, should restart instead of start when unit changes under an active service", calls) } + info, err := os.Stat(path) + if err != nil { + t.Fatalf("Stat(%q): %v", path, err) + } + if got := info.Mode().Perm(); got != 0o600 { + t.Fatalf("systemd unit mode after warm upgrade = %03o, want 600", got) + } +} + +func TestInstallSupervisorSystemdWritesPrivateUnitFile(t *testing.T) { + if goruntime.GOOS != "linux" { + t.Skip("systemd path only applies on linux") + } + homeDir := t.TempDir() + t.Setenv("HOME", homeDir) + t.Setenv("GC_HOME", filepath.Join(homeDir, ".gc")) + + data := &supervisorServiceData{ + GCPath: "/tmp/gc-new", + LogPath: "/tmp/gc-home/supervisor.log", + GCHome: "/tmp/gc-home", + Path: "/usr/local/bin:/usr/bin:/bin", + ExtraEnv: []supervisorServiceEnvVar{ + {Name: "OPENAI_API_KEY", Value: "sk-openai-123"}, + }, + } + + oldRun := supervisorSystemctlRun + oldActive := supervisorSystemctlActive + supervisorSystemctlRun = func(_ ...string) error { + return nil + } + supervisorSystemctlActive = func(_ string) bool { + return false + } + t.Cleanup(func() { + supervisorSystemctlRun = oldRun + supervisorSystemctlActive = oldActive + }) + + var stdout, stderr bytes.Buffer + if code := installSupervisorSystemd(data, &stdout, &stderr); code != 0 { + t.Fatalf("installSupervisorSystemd code = %d, want 0; stderr=%q", code, stderr.String()) + } + info, err := os.Stat(supervisorSystemdServicePath()) + if err != nil { + t.Fatalf("Stat(%q): %v", supervisorSystemdServicePath(), err) + } + if got := info.Mode().Perm(); got != 0o600 { + t.Fatalf("systemd unit mode = %03o, want 600", got) + } } func TestInstallSupervisorSystemdStartsInactiveService(t *testing.T) { @@ -1187,6 +1303,13 @@ func TestInstallSupervisorSystemdRestoresPreviousCurrentUnitWhenUpdateFails(t *t if !bytes.Equal(gotContent, oldContent) { t.Fatalf("restored systemd unit = %q, want original %q", gotContent, oldContent) } + info, err := os.Stat(currentPath) + if err != nil { + t.Fatalf("Stat(%q): %v", currentPath, err) + } + if got := info.Mode().Perm(); got != 0o600 { + t.Fatalf("restored systemd unit mode = %03o, want 600", got) + } if startCalls != 2 { t.Fatalf("systemctl start call count = %d, want 2 (failed install + rollback restore); calls=%v", startCalls, calls) } @@ -1353,6 +1476,45 @@ func TestInstallSupervisorLaunchdRemovesMatchingLegacyDefaultPlistForIsolatedGCH } } +func TestInstallSupervisorLaunchdWritesPrivatePlist(t *testing.T) { + homeDir := t.TempDir() + gcHome := filepath.Join(t.TempDir(), "isolated-home") + t.Setenv("HOME", homeDir) + t.Setenv("GC_HOME", gcHome) + + data := &supervisorServiceData{ + GCPath: "/tmp/gc-new", + LogPath: filepath.Join(gcHome, "supervisor.log"), + GCHome: gcHome, + LaunchdLabel: supervisorLaunchdLabel(), + Path: "/usr/local/bin:/usr/bin:/bin", + ExtraEnv: []supervisorServiceEnvVar{ + {Name: "OPENAI_API_KEY", Value: "sk-openai-123"}, + }, + } + + oldRun := supervisorLaunchctlRun + supervisorLaunchctlRun = func(_ ...string) error { + return nil + } + t.Cleanup(func() { + supervisorLaunchctlRun = oldRun + }) + + var stdout, stderr bytes.Buffer + if code := installSupervisorLaunchd(data, &stdout, &stderr); code != 0 { + t.Fatalf("installSupervisorLaunchd code = %d, want 0; stderr=%q", code, stderr.String()) + } + path := supervisorLaunchdPlistPath() + info, err := os.Stat(path) + if err != nil { + t.Fatalf("Stat(%q): %v", path, err) + } + if got := info.Mode().Perm(); got != 0o600 { + t.Fatalf("launchd plist mode = %03o, want 600", got) + } +} + func TestInstallSupervisorLaunchdIgnoresLegacyUnloadFailures(t *testing.T) { homeDir := t.TempDir() gcHome := filepath.Join(t.TempDir(), "isolated-home") @@ -1527,6 +1689,13 @@ func TestInstallSupervisorLaunchdRestoresPreviousCurrentPlistWhenUpdateFails(t * if !bytes.Equal(gotContent, oldContent) { t.Fatalf("restored launchd plist = %q, want original %q", gotContent, oldContent) } + info, err := os.Stat(currentPath) + if err != nil { + t.Fatalf("Stat(%q): %v", currentPath, err) + } + if got := info.Mode().Perm(); got != 0o600 { + t.Fatalf("restored launchd plist mode = %03o, want 600", got) + } if loadCalls != 2 { t.Fatalf("launchctl load call count = %d, want 2 (failed install + rollback restore); calls=%v", loadCalls, calls) } diff --git a/cmd/gc/cmd_wait_test.go b/cmd/gc/cmd_wait_test.go index 387f50fea7..5a52123a6d 100644 --- a/cmd/gc/cmd_wait_test.go +++ b/cmd/gc/cmd_wait_test.go @@ -72,7 +72,7 @@ func waitTestEnv(overrides map[string]string) []string { func waitTestRealBDPath(t *testing.T) string { t.Helper() - skipSlowCmdGCTest(t, "requires a managed bd lifecycle city; run without -short or via integration packages") + skipSlowCmdGCTest(t, "requires a managed bd lifecycle city; run make test-cmd-gc-process for full coverage") waitTestRealBDPathOnce.Do(func() { for _, dir := range filepath.SplitList(os.Getenv("PATH")) { if strings.TrimSpace(dir) == "" { @@ -1270,6 +1270,7 @@ func setupFreshManagedBdWaitTestCity(t *testing.T) (string, string) { func setupManagedBdWaitTestCity(t *testing.T) (string, string) { t.Helper() + skipSlowCmdGCTest(t, "requires a managed bd/dolt lifecycle city; run make test-cmd-gc-process for full coverage") configureIsolatedRuntimeEnv(t) bdPath := waitTestRealBDPath(t) diff --git a/cmd/gc/controller.go b/cmd/gc/controller.go index 862a011c19..d71e2afbc4 100644 --- a/cmd/gc/controller.go +++ b/cmd/gc/controller.go @@ -1135,6 +1135,7 @@ func runController( cs := newControllerState(ctx, cfg, sp, eventProv, cityName, cityPath) cs.ct = cr.crashTrack() cs.pokeCh = pokeCh + cs.configDirty = configDirty cs.services = cr.svc cs.startBeadEventWatcher(ctx) cr.setControllerState(cs) @@ -1151,7 +1152,11 @@ func runController( if readOnly { fmt.Fprintf(stderr, "api: binding to %s — mutation endpoints disabled (non-localhost)\n", bind) //nolint:errcheck } - apiMux := api.NewSupervisorMux(&singleCityStateResolver{state: cs}, readOnly, "controller", time.Now()) + // Standalone controller mode serves one existing city. It does + // not own the supervisor registry/reconciler path required by + // async POST /v0/city, so leave the initializer nil and let the + // handler return 501 for create/unregister routes. + apiMux := api.NewSupervisorMux(&singleCityStateResolver{state: cs}, nil, readOnly, "controller", time.Now()) addr := net.JoinHostPort(bind, strconv.Itoa(cfg.API.Port)) apiLis, apiErr := net.Listen("tcp", addr) if apiErr != nil { diff --git a/cmd/gc/dashboard/web/dist/dashboard.js b/cmd/gc/dashboard/web/dist/dashboard.js index ad2e593c11..ec9931ec7b 100644 --- a/cmd/gc/dashboard/web/dist/dashboard.js +++ b/cmd/gc/dashboard/web/dist/dashboard.js @@ -1,6 +1,6 @@ -(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))a(r);new MutationObserver(r=>{for(const i of r)if(i.type==="childList")for(const o of i.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&a(o)}).observe(document,{childList:!0,subtree:!0});function n(r){const i={};return r.integrity&&(i.integrity=r.integrity),r.referrerPolicy&&(i.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?i.credentials="include":r.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function a(r){if(r.ep)return;r.ep=!0;const i=n(r);fetch(r.href,i)}})();const En=/\{[^{}]+\}/g,kn=()=>{var e,t;return typeof process=="object"&&Number.parseInt((t=(e=process==null?void 0:process.versions)==null?void 0:e.node)==null?void 0:t.substring(0,2))>=18&&process.versions.undici};function Nn(){return Math.random().toString(36).slice(2,11)}function $n(e){let{baseUrl:t="",Request:n=globalThis.Request,fetch:a=globalThis.fetch,querySerializer:r,bodySerializer:i,headers:o,requestInitExt:l=void 0,...d}={...e};l=kn()?l:void 0,t=Nt(t);const p=[];async function f(u,y){const{baseUrl:m,fetch:h=a,Request:w=n,headers:C,params:b={},parseAs:E="json",querySerializer:N,bodySerializer:j=i??xn,body:B,...$}=y||{};let R=t;m&&(R=Nt(m)??t);let T=typeof r=="function"?r:Et(r);N&&(T=typeof N=="function"?N:Et({...typeof r=="object"?r:{},...N}));const ue=B===void 0?void 0:j(B,kt(o,C,b.header)),Ye=kt(ue===void 0||ue instanceof FormData?{}:{"Content-Type":"application/json"},o,C,b.header),Ze={redirect:"follow",...d,...$,body:ue,headers:Ye};let ee,fe,G=new n(Tn(u,{baseUrl:R,params:b,querySerializer:T}),Ze),x;for(const A in $)A in G||(G[A]=$[A]);if(p.length){ee=Nn(),fe=Object.freeze({baseUrl:R,fetch:h,parseAs:E,querySerializer:T,bodySerializer:j});for(const A of p)if(A&&typeof A=="object"&&typeof A.onRequest=="function"){const O=await A.onRequest({request:G,schemaPath:u,params:b,options:fe,id:ee});if(O)if(O instanceof n)G=O;else if(O instanceof Response){x=O;break}else throw new Error("onRequest: must return new Request() or Response() when modifying the request")}}if(!x){try{x=await h(G,l)}catch(A){let O=A;if(p.length)for(let q=p.length-1;q>=0;q--){const te=p[q];if(te&&typeof te=="object"&&typeof te.onError=="function"){const Ce=await te.onError({request:G,error:O,schemaPath:u,params:b,options:fe,id:ee});if(Ce){if(Ce instanceof Response){O=void 0,x=Ce;break}if(Ce instanceof Error){O=Ce;continue}throw new Error("onError: must return new Response() or instance of Error")}}}if(O)throw O}if(p.length)for(let A=p.length-1;A>=0;A--){const O=p[A];if(O&&typeof O=="object"&&typeof O.onResponse=="function"){const q=await O.onResponse({request:G,response:x,schemaPath:u,params:b,options:fe,id:ee});if(q){if(!(q instanceof Response))throw new Error("onResponse: must return new Response() when modifying the response");x=q}}}}if(x.status===204||G.method==="HEAD"||x.headers.get("Content-Length")==="0")return x.ok?{data:void 0,response:x}:{error:void 0,response:x};if(x.ok)return E==="stream"?{data:x.body,response:x}:{data:await x[E](),response:x};let pe=await x.text();try{pe=JSON.parse(pe)}catch{}return{error:pe,response:x}}return{request(u,y,m){return f(y,{...m,method:u.toUpperCase()})},GET(u,y){return f(u,{...y,method:"GET"})},PUT(u,y){return f(u,{...y,method:"PUT"})},POST(u,y){return f(u,{...y,method:"POST"})},DELETE(u,y){return f(u,{...y,method:"DELETE"})},OPTIONS(u,y){return f(u,{...y,method:"OPTIONS"})},HEAD(u,y){return f(u,{...y,method:"HEAD"})},PATCH(u,y){return f(u,{...y,method:"PATCH"})},TRACE(u,y){return f(u,{...y,method:"TRACE"})},use(...u){for(const y of u)if(y){if(typeof y!="object"||!("onRequest"in y||"onResponse"in y||"onError"in y))throw new Error("Middleware must be an object with one of `onRequest()`, `onResponse() or `onError()`");p.push(y)}},eject(...u){for(const y of u){const m=p.indexOf(y);m!==-1&&p.splice(m,1)}}}}function Fe(e,t,n){if(t==null)return"";if(typeof t=="object")throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");return`${e}=${(n==null?void 0:n.allowReserved)===!0?t:encodeURIComponent(t)}`}function Pt(e,t,n){if(!t||typeof t!="object")return"";const a=[],r={simple:",",label:".",matrix:";"}[n.style]||"&";if(n.style!=="deepObject"&&n.explode===!1){for(const l in t)a.push(l,n.allowReserved===!0?t[l]:encodeURIComponent(t[l]));const o=a.join(",");switch(n.style){case"form":return`${e}=${o}`;case"label":return`.${o}`;case"matrix":return`;${e}=${o}`;default:return o}}for(const o in t){const l=n.style==="deepObject"?`${e}[${o}]`:o;a.push(Fe(l,t[o],n))}const i=a.join(r);return n.style==="label"||n.style==="matrix"?`${r}${i}`:i}function jt(e,t,n){if(!Array.isArray(t))return"";if(n.explode===!1){const i={form:",",spaceDelimited:"%20",pipeDelimited:"|"}[n.style]||",",o=(n.allowReserved===!0?t:t.map(l=>encodeURIComponent(l))).join(i);switch(n.style){case"simple":return o;case"label":return`.${o}`;case"matrix":return`;${e}=${o}`;default:return`${e}=${o}`}}const a={simple:",",label:".",matrix:";"}[n.style]||"&",r=[];for(const i of t)n.style==="simple"||n.style==="label"?r.push(n.allowReserved===!0?i:encodeURIComponent(i)):r.push(Fe(e,i,n));return n.style==="label"||n.style==="matrix"?`${a}${r.join(a)}`:r.join(a)}function Et(e){return function(n){const a=[];if(n&&typeof n=="object")for(const r in n){const i=n[r];if(i!=null){if(Array.isArray(i)){if(i.length===0)continue;a.push(jt(r,i,{style:"form",explode:!0,...e==null?void 0:e.array,allowReserved:(e==null?void 0:e.allowReserved)||!1}));continue}if(typeof i=="object"){a.push(Pt(r,i,{style:"deepObject",explode:!0,...e==null?void 0:e.object,allowReserved:(e==null?void 0:e.allowReserved)||!1}));continue}a.push(Fe(r,i,e))}}return a.join("&")}}function Ln(e,t){let n=e;for(const a of e.match(En)??[]){let r=a.substring(1,a.length-1),i=!1,o="simple";if(r.endsWith("*")&&(i=!0,r=r.substring(0,r.length-1)),r.startsWith(".")?(o="label",r=r.substring(1)):r.startsWith(";")&&(o="matrix",r=r.substring(1)),!t||t[r]===void 0||t[r]===null)continue;const l=t[r];if(Array.isArray(l)){n=n.replace(a,jt(r,l,{style:o,explode:i}));continue}if(typeof l=="object"){n=n.replace(a,Pt(r,l,{style:o,explode:i}));continue}if(o==="matrix"){n=n.replace(a,`;${Fe(r,l)}`);continue}n=n.replace(a,o==="label"?`.${encodeURIComponent(l)}`:encodeURIComponent(l))}return n}function xn(e,t){return e instanceof FormData?e:t&&(t.get instanceof Function?t.get("Content-Type")??t.get("content-type"):t["Content-Type"]??t["content-type"])==="application/x-www-form-urlencoded"?new URLSearchParams(e).toString():JSON.stringify(e)}function Tn(e,t){var r;let n=`${t.baseUrl}${e}`;(r=t.params)!=null&&r.path&&(n=Ln(n,t.params.path));let a=t.querySerializer(t.params.query??{});return a.startsWith("?")&&(a=a.substring(1)),a&&(n+=`?${a}`),n}function kt(...e){const t=new Headers;for(const n of e){if(!n||typeof n!="object")continue;const a=n instanceof Headers?n.entries():Object.entries(n);for(const[r,i]of a)if(i===null)t.delete(r);else if(Array.isArray(i))for(const o of i)t.append(r,o);else i!==void 0&&t.set(r,i)}return t}function Nt(e){return e.endsWith("/")?e.substring(0,e.length-1):e}const An={bodySerializer:e=>JSON.stringify(e,(t,n)=>typeof n=="bigint"?n.toString():n)};function Rn({onRequest:e,onSseError:t,onSseEvent:n,responseTransformer:a,responseValidator:r,sseDefaultRetryDelay:i,sseMaxRetryAttempts:o,sseMaxRetryDelay:l,sseSleepFn:d,url:p,...f}){let u;const y=d??(w=>new Promise(C=>setTimeout(C,w)));return{stream:async function*(){let w=i??3e3,C=0;const b=f.signal??new AbortController().signal;for(;!b.aborted;){C++;const E=f.headers instanceof Headers?f.headers:new Headers(f.headers);u!==void 0&&E.set("Last-Event-ID",u);try{const N={redirect:"follow",...f,body:f.serializedBody,headers:E,signal:b};let j=new Request(p,N);e&&(j=await e(p,N));const $=await(f.fetch??globalThis.fetch)(j);if(!$.ok)throw new Error(`SSE failed: ${$.status} ${$.statusText}`);if(!$.body)throw new Error("No body in SSE response");const R=$.body.pipeThrough(new TextDecoderStream).getReader();let T="";const ue=()=>{try{R.cancel()}catch{}};b.addEventListener("abort",ue);try{for(;;){const{done:Ye,value:Ze}=await R.read();if(Ye)break;T+=Ze,T=T.replace(/\r\n?/g,` -`);const ee=T.split(` +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))a(r);new MutationObserver(r=>{for(const i of r)if(i.type==="childList")for(const o of i.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&a(o)}).observe(document,{childList:!0,subtree:!0});function n(r){const i={};return r.integrity&&(i.integrity=r.integrity),r.referrerPolicy&&(i.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?i.credentials="include":r.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function a(r){if(r.ep)return;r.ep=!0;const i=n(r);fetch(r.href,i)}})();const kn=/\{[^{}]+\}/g,Nn=()=>{var e,t;return typeof process=="object"&&Number.parseInt((t=(e=process==null?void 0:process.versions)==null?void 0:e.node)==null?void 0:t.substring(0,2))>=18&&process.versions.undici};function $n(){return Math.random().toString(36).slice(2,11)}function Ln(e){let{baseUrl:t="",Request:n=globalThis.Request,fetch:a=globalThis.fetch,querySerializer:r,bodySerializer:i,headers:o,requestInitExt:l=void 0,...d}={...e};l=Nn()?l:void 0,t=$t(t);const p=[];async function f(u,y){const{baseUrl:m,fetch:h=a,Request:w=n,headers:E,params:b={},parseAs:C="json",querySerializer:N,bodySerializer:I=i??Tn,body:M,...$}=y||{};let O=t;m&&(O=$t(m)??t);let A=typeof r=="function"?r:kt(r);N&&(A=typeof N=="function"?N:kt({...typeof r=="object"?r:{},...N}));const fe=M===void 0?void 0:I(M,Nt(o,E,b.header)),Ze=Nt(fe===void 0||fe instanceof FormData?{}:{"Content-Type":"application/json"},o,E,b.header),et={redirect:"follow",...d,...$,body:fe,headers:Ze};let te,pe,G=new n(An(u,{baseUrl:O,params:b,querySerializer:A}),et),x;for(const R in $)R in G||(G[R]=$[R]);if(p.length){te=$n(),pe=Object.freeze({baseUrl:O,fetch:h,parseAs:C,querySerializer:A,bodySerializer:I});for(const R of p)if(R&&typeof R=="object"&&typeof R.onRequest=="function"){const q=await R.onRequest({request:G,schemaPath:u,params:b,options:pe,id:te});if(q)if(q instanceof n)G=q;else if(q instanceof Response){x=q;break}else throw new Error("onRequest: must return new Request() or Response() when modifying the request")}}if(!x){try{x=await h(G,l)}catch(R){let q=R;if(p.length)for(let _=p.length-1;_>=0;_--){const ne=p[_];if(ne&&typeof ne=="object"&&typeof ne.onError=="function"){const Ce=await ne.onError({request:G,error:q,schemaPath:u,params:b,options:pe,id:te});if(Ce){if(Ce instanceof Response){q=void 0,x=Ce;break}if(Ce instanceof Error){q=Ce;continue}throw new Error("onError: must return new Response() or instance of Error")}}}if(q)throw q}if(p.length)for(let R=p.length-1;R>=0;R--){const q=p[R];if(q&&typeof q=="object"&&typeof q.onResponse=="function"){const _=await q.onResponse({request:G,response:x,schemaPath:u,params:b,options:pe,id:te});if(_){if(!(_ instanceof Response))throw new Error("onResponse: must return new Response() when modifying the response");x=_}}}}if(x.status===204||G.method==="HEAD"||x.headers.get("Content-Length")==="0")return x.ok?{data:void 0,response:x}:{error:void 0,response:x};if(x.ok)return C==="stream"?{data:x.body,response:x}:{data:await x[C](),response:x};let ye=await x.text();try{ye=JSON.parse(ye)}catch{}return{error:ye,response:x}}return{request(u,y,m){return f(y,{...m,method:u.toUpperCase()})},GET(u,y){return f(u,{...y,method:"GET"})},PUT(u,y){return f(u,{...y,method:"PUT"})},POST(u,y){return f(u,{...y,method:"POST"})},DELETE(u,y){return f(u,{...y,method:"DELETE"})},OPTIONS(u,y){return f(u,{...y,method:"OPTIONS"})},HEAD(u,y){return f(u,{...y,method:"HEAD"})},PATCH(u,y){return f(u,{...y,method:"PATCH"})},TRACE(u,y){return f(u,{...y,method:"TRACE"})},use(...u){for(const y of u)if(y){if(typeof y!="object"||!("onRequest"in y||"onResponse"in y||"onError"in y))throw new Error("Middleware must be an object with one of `onRequest()`, `onResponse() or `onError()`");p.push(y)}},eject(...u){for(const y of u){const m=p.indexOf(y);m!==-1&&p.splice(m,1)}}}}function He(e,t,n){if(t==null)return"";if(typeof t=="object")throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");return`${e}=${(n==null?void 0:n.allowReserved)===!0?t:encodeURIComponent(t)}`}function jt(e,t,n){if(!t||typeof t!="object")return"";const a=[],r={simple:",",label:".",matrix:";"}[n.style]||"&";if(n.style!=="deepObject"&&n.explode===!1){for(const l in t)a.push(l,n.allowReserved===!0?t[l]:encodeURIComponent(t[l]));const o=a.join(",");switch(n.style){case"form":return`${e}=${o}`;case"label":return`.${o}`;case"matrix":return`;${e}=${o}`;default:return o}}for(const o in t){const l=n.style==="deepObject"?`${e}[${o}]`:o;a.push(He(l,t[o],n))}const i=a.join(r);return n.style==="label"||n.style==="matrix"?`${r}${i}`:i}function It(e,t,n){if(!Array.isArray(t))return"";if(n.explode===!1){const i={form:",",spaceDelimited:"%20",pipeDelimited:"|"}[n.style]||",",o=(n.allowReserved===!0?t:t.map(l=>encodeURIComponent(l))).join(i);switch(n.style){case"simple":return o;case"label":return`.${o}`;case"matrix":return`;${e}=${o}`;default:return`${e}=${o}`}}const a={simple:",",label:".",matrix:";"}[n.style]||"&",r=[];for(const i of t)n.style==="simple"||n.style==="label"?r.push(n.allowReserved===!0?i:encodeURIComponent(i)):r.push(He(e,i,n));return n.style==="label"||n.style==="matrix"?`${a}${r.join(a)}`:r.join(a)}function kt(e){return function(n){const a=[];if(n&&typeof n=="object")for(const r in n){const i=n[r];if(i!=null){if(Array.isArray(i)){if(i.length===0)continue;a.push(It(r,i,{style:"form",explode:!0,...e==null?void 0:e.array,allowReserved:(e==null?void 0:e.allowReserved)||!1}));continue}if(typeof i=="object"){a.push(jt(r,i,{style:"deepObject",explode:!0,...e==null?void 0:e.object,allowReserved:(e==null?void 0:e.allowReserved)||!1}));continue}a.push(He(r,i,e))}}return a.join("&")}}function xn(e,t){let n=e;for(const a of e.match(kn)??[]){let r=a.substring(1,a.length-1),i=!1,o="simple";if(r.endsWith("*")&&(i=!0,r=r.substring(0,r.length-1)),r.startsWith(".")?(o="label",r=r.substring(1)):r.startsWith(";")&&(o="matrix",r=r.substring(1)),!t||t[r]===void 0||t[r]===null)continue;const l=t[r];if(Array.isArray(l)){n=n.replace(a,It(r,l,{style:o,explode:i}));continue}if(typeof l=="object"){n=n.replace(a,jt(r,l,{style:o,explode:i}));continue}if(o==="matrix"){n=n.replace(a,`;${He(r,l)}`);continue}n=n.replace(a,o==="label"?`.${encodeURIComponent(l)}`:encodeURIComponent(l))}return n}function Tn(e,t){return e instanceof FormData?e:t&&(t.get instanceof Function?t.get("Content-Type")??t.get("content-type"):t["Content-Type"]??t["content-type"])==="application/x-www-form-urlencoded"?new URLSearchParams(e).toString():JSON.stringify(e)}function An(e,t){var r;let n=`${t.baseUrl}${e}`;(r=t.params)!=null&&r.path&&(n=xn(n,t.params.path));let a=t.querySerializer(t.params.query??{});return a.startsWith("?")&&(a=a.substring(1)),a&&(n+=`?${a}`),n}function Nt(...e){const t=new Headers;for(const n of e){if(!n||typeof n!="object")continue;const a=n instanceof Headers?n.entries():Object.entries(n);for(const[r,i]of a)if(i===null)t.delete(r);else if(Array.isArray(i))for(const o of i)t.append(r,o);else i!==void 0&&t.set(r,i)}return t}function $t(e){return e.endsWith("/")?e.substring(0,e.length-1):e}const Rn={bodySerializer:e=>JSON.stringify(e,(t,n)=>typeof n=="bigint"?n.toString():n)};function On({onRequest:e,onSseError:t,onSseEvent:n,responseTransformer:a,responseValidator:r,sseDefaultRetryDelay:i,sseMaxRetryAttempts:o,sseMaxRetryDelay:l,sseSleepFn:d,url:p,...f}){let u;const y=d??(w=>new Promise(E=>setTimeout(E,w)));return{stream:async function*(){let w=i??3e3,E=0;const b=f.signal??new AbortController().signal;for(;!b.aborted;){E++;const C=f.headers instanceof Headers?f.headers:new Headers(f.headers);u!==void 0&&C.set("Last-Event-ID",u);try{const N={redirect:"follow",...f,body:f.serializedBody,headers:C,signal:b};let I=new Request(p,N);e&&(I=await e(p,N));const $=await(f.fetch??globalThis.fetch)(I);if(!$.ok)throw new Error(`SSE failed: ${$.status} ${$.statusText}`);if(!$.body)throw new Error("No body in SSE response");const O=$.body.pipeThrough(new TextDecoderStream).getReader();let A="";const fe=()=>{try{O.cancel()}catch{}};b.addEventListener("abort",fe);try{for(;;){const{done:Ze,value:et}=await O.read();if(Ze)break;A+=et,A=A.replace(/\r\n?/g,` +`);const te=A.split(` -`);T=ee.pop()??"";for(const fe of ee){const G=fe.split(` -`),x=[];let pe;for(const q of G)if(q.startsWith("data:"))x.push(q.replace(/^data:\s*/,""));else if(q.startsWith("event:"))pe=q.replace(/^event:\s*/,"");else if(q.startsWith("id:"))u=q.replace(/^id:\s*/,"");else if(q.startsWith("retry:")){const te=Number.parseInt(q.replace(/^retry:\s*/,""),10);Number.isNaN(te)||(w=te)}let A,O=!1;if(x.length){const q=x.join(` -`);try{A=JSON.parse(q),O=!0}catch{A=q}}O&&(r&&await r(A),a&&(A=await a(A))),n==null||n({data:A,event:pe,id:u,retry:w}),x.length&&(yield A)}}}finally{b.removeEventListener("abort",ue),R.releaseLock()}break}catch(N){if(t==null||t(N),o!==void 0&&C>=o)break;const j=Math.min(w*2**(C-1),l??3e4);await y(j)}}}()}}const On=e=>{switch(e){case"label":return".";case"matrix":return";";case"simple":return",";default:return"&"}},qn=e=>{switch(e){case"form":return",";case"pipeDelimited":return"|";case"spaceDelimited":return"%20";default:return","}},_n=e=>{switch(e){case"label":return".";case"matrix":return";";case"simple":return",";default:return"&"}},It=({allowReserved:e,explode:t,name:n,style:a,value:r})=>{if(!t){const l=(e?r:r.map(d=>encodeURIComponent(d))).join(qn(a));switch(a){case"label":return`.${l}`;case"matrix":return`;${n}=${l}`;case"simple":return l;default:return`${n}=${l}`}}const i=On(a),o=r.map(l=>a==="label"||a==="simple"?e?l:encodeURIComponent(l):He({allowReserved:e,name:n,value:l})).join(i);return a==="label"||a==="matrix"?i+o:o},He=({allowReserved:e,name:t,value:n})=>{if(n==null)return"";if(typeof n=="object")throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");return`${t}=${e?n:encodeURIComponent(n)}`},Bt=({allowReserved:e,explode:t,name:n,style:a,value:r,valueOnly:i})=>{if(r instanceof Date)return i?r.toISOString():`${n}=${r.toISOString()}`;if(a!=="deepObject"&&!t){let d=[];Object.entries(r).forEach(([f,u])=>{d=[...d,f,e?u:encodeURIComponent(u)]});const p=d.join(",");switch(a){case"form":return`${n}=${p}`;case"label":return`.${p}`;case"matrix":return`;${n}=${p}`;default:return p}}const o=_n(a),l=Object.entries(r).map(([d,p])=>He({allowReserved:e,name:a==="deepObject"?`${n}[${d}]`:d,value:p})).join(o);return a==="label"||a==="matrix"?o+l:l},Pn=/\{[^{}]+\}/g,jn=({path:e,url:t})=>{let n=t;const a=t.match(Pn);if(a)for(const r of a){let i=!1,o=r.substring(1,r.length-1),l="simple";o.endsWith("*")&&(i=!0,o=o.substring(0,o.length-1)),o.startsWith(".")?(o=o.substring(1),l="label"):o.startsWith(";")&&(o=o.substring(1),l="matrix");const d=e[o];if(d==null)continue;if(Array.isArray(d)){n=n.replace(r,It({explode:i,name:o,style:l,value:d}));continue}if(typeof d=="object"){n=n.replace(r,Bt({explode:i,name:o,style:l,value:d,valueOnly:!0}));continue}if(l==="matrix"){n=n.replace(r,`;${He({name:o,value:d})}`);continue}const p=encodeURIComponent(l==="label"?`.${d}`:d);n=n.replace(r,p)}return n},In=({baseUrl:e,path:t,query:n,querySerializer:a,url:r})=>{const i=r.startsWith("/")?r:`/${r}`;let o=(e??"")+i;t&&(o=jn({path:t,url:o}));let l=n?a(n):"";return l.startsWith("?")&&(l=l.substring(1)),l&&(o+=`?${l}`),o};function $t(e){const t=e.body!==void 0;if(t&&e.bodySerializer)return"serializedBody"in e?e.serializedBody!==void 0&&e.serializedBody!==""?e.serializedBody:null:e.body!==""?e.body:null;if(t)return e.body}const Bn=async(e,t)=>{const n=typeof t=="function"?await t(e):t;if(n)return e.scheme==="bearer"?`Bearer ${n}`:e.scheme==="basic"?`Basic ${btoa(n)}`:n},Mt=({parameters:e={},...t}={})=>a=>{const r=[];if(a&&typeof a=="object")for(const i in a){const o=a[i];if(o==null)continue;const l=e[i]||t;if(Array.isArray(o)){const d=It({allowReserved:l.allowReserved,explode:!0,name:i,style:"form",value:o,...l.array});d&&r.push(d)}else if(typeof o=="object"){const d=Bt({allowReserved:l.allowReserved,explode:!0,name:i,style:"deepObject",value:o,...l.object});d&&r.push(d)}else{const d=He({allowReserved:l.allowReserved,name:i,value:o});d&&r.push(d)}}return r.join("&")},Mn=e=>{var n;if(!e)return"stream";const t=(n=e.split(";")[0])==null?void 0:n.trim();if(t){if(t.startsWith("application/json")||t.endsWith("+json"))return"json";if(t==="multipart/form-data")return"formData";if(["application/","audio/","image/","video/"].some(a=>t.startsWith(a)))return"blob";if(t.startsWith("text/"))return"text"}},Un=(e,t)=>{var n,a;return t?!!(e.headers.has(t)||(n=e.query)!=null&&n[t]||(a=e.headers.get("Cookie"))!=null&&a.includes(`${t}=`)):!1},Dn=async({security:e,...t})=>{for(const n of e){if(Un(t,n.name))continue;const a=await Bn(n,t.auth);if(!a)continue;const r=n.name??"Authorization";switch(n.in){case"query":t.query||(t.query={}),t.query[r]=a;break;case"cookie":t.headers.append("Cookie",`${r}=${a}`);break;case"header":default:t.headers.set(r,a);break}}},Lt=e=>In({baseUrl:e.baseUrl,path:e.path,query:e.query,querySerializer:typeof e.querySerializer=="function"?e.querySerializer:Mt(e.querySerializer),url:e.url}),xt=(e,t)=>{var a;const n={...e,...t};return(a=n.baseUrl)!=null&&a.endsWith("/")&&(n.baseUrl=n.baseUrl.substring(0,n.baseUrl.length-1)),n.headers=Ut(e.headers,t.headers),n},zn=e=>{const t=[];return e.forEach((n,a)=>{t.push([a,n])}),t},Ut=(...e)=>{const t=new Headers;for(const n of e){if(!n)continue;const a=n instanceof Headers?zn(n):Object.entries(n);for(const[r,i]of a)if(i===null)t.delete(r);else if(Array.isArray(i))for(const o of i)t.append(r,o);else i!==void 0&&t.set(r,typeof i=="object"?JSON.stringify(i):i)}return t};class et{constructor(){this.fns=[]}clear(){this.fns=[]}eject(t){const n=this.getInterceptorIndex(t);this.fns[n]&&(this.fns[n]=null)}exists(t){const n=this.getInterceptorIndex(t);return!!this.fns[n]}getInterceptorIndex(t){return typeof t=="number"?this.fns[t]?t:-1:this.fns.indexOf(t)}update(t,n){const a=this.getInterceptorIndex(t);return this.fns[a]?(this.fns[a]=n,t):!1}use(t){return this.fns.push(t),this.fns.length-1}}const Gn=()=>({error:new et,request:new et,response:new et}),Wn=Mt({allowReserved:!1,array:{explode:!0,style:"form"},object:{explode:!0,style:"deepObject"}}),Fn={"Content-Type":"application/json"},Dt=(e={})=>({...An,headers:Fn,parseAs:"auto",querySerializer:Wn,...e}),Hn=(e={})=>{let t=xt(Dt(),e);const n=()=>({...t}),a=f=>(t=xt(t,f),n()),r=Gn(),i=async f=>{const u={...t,...f,fetch:f.fetch??t.fetch??globalThis.fetch,headers:Ut(t.headers,f.headers),serializedBody:void 0};u.security&&await Dn({...u,security:u.security}),u.requestValidator&&await u.requestValidator(u),u.body!==void 0&&u.bodySerializer&&(u.serializedBody=u.bodySerializer(u.body)),(u.body===void 0||u.serializedBody==="")&&u.headers.delete("Content-Type");const y=u,m=Lt(y);return{opts:y,url:m}},o=async f=>{const{opts:u,url:y}=await i(f),m={redirect:"follow",...u,body:$t(u)};let h=new Request(y,m);for(const $ of r.request.fns)$&&(h=await $(h,u));const w=u.fetch;let C;try{C=await w(h)}catch($){let R=$;for(const T of r.error.fns)T&&(R=await T($,void 0,h,u));if(R=R||{},u.throwOnError)throw R;return u.responseStyle==="data"?void 0:{error:R,request:h,response:void 0}}for(const $ of r.response.fns)$&&(C=await $(C,h,u));const b={request:h,response:C};if(C.ok){const $=(u.parseAs==="auto"?Mn(C.headers.get("Content-Type")):u.parseAs)??"json";if(C.status===204||C.headers.get("Content-Length")==="0"){let T;switch($){case"arrayBuffer":case"blob":case"text":T=await C[$]();break;case"formData":T=new FormData;break;case"stream":T=C.body;break;case"json":default:T={};break}return u.responseStyle==="data"?T:{data:T,...b}}let R;switch($){case"arrayBuffer":case"blob":case"formData":case"text":R=await C[$]();break;case"json":{const T=await C.text();R=T?JSON.parse(T):{};break}case"stream":return u.responseStyle==="data"?C.body:{data:C.body,...b}}return $==="json"&&(u.responseValidator&&await u.responseValidator(R),u.responseTransformer&&(R=await u.responseTransformer(R))),u.responseStyle==="data"?R:{data:R,...b}}const E=await C.text();let N;try{N=JSON.parse(E)}catch{}const j=N??E;let B=j;for(const $ of r.error.fns)$&&(B=await $(j,C,h,u));if(B=B||{},u.throwOnError)throw B;return u.responseStyle==="data"?void 0:{error:B,...b}},l=f=>u=>o({...u,method:f}),d=f=>async u=>{const{opts:y,url:m}=await i(u);return Rn({...y,body:y.body,headers:y.headers,method:f,onRequest:async(h,w)=>{let C=new Request(h,w);for(const b of r.request.fns)b&&(C=await b(C,y));return C},serializedBody:$t(y),url:m})};return{buildUrl:f=>Lt({...t,...f}),connect:l("CONNECT"),delete:l("DELETE"),get:l("GET"),getConfig:n,head:l("HEAD"),interceptors:r,options:l("OPTIONS"),patch:l("PATCH"),post:l("POST"),put:l("PUT"),request:o,setConfig:a,sse:{connect:d("CONNECT"),delete:d("DELETE"),get:d("GET"),head:d("HEAD"),options:d("OPTIONS"),patch:d("PATCH"),post:d("POST"),put:d("PUT"),trace:d("TRACE")},trace:l("TRACE")}},ce=Hn(Dt()),zt={debug:console.debug.bind(console),error:console.error.bind(console),info:console.info.bind(console),log:console.log.bind(console),warn:console.warn.bind(console)};let Tt=!1;function Jn(){Tt||typeof window>"u"||(Tt=!0,Ee("debug","debug"),Ee("info","info"),Ee("warn","warn"),Ee("error","error"),Ee("log","info"),window.addEventListener("error",e=>{ie("window","Unhandled error",{colno:e.colno,error:e.error,filename:e.filename,lineno:e.lineno,message:e.message})}),window.addEventListener("unhandledrejection",e=>{ie("window","Unhandled promise rejection",{reason:e.reason})}))}function be(e,t,n){Ve("debug",e,t,n)}function H(e,t,n){Ve("info",e,t,n)}function Je(e,t,n){Ve("warn",e,t,n)}function ie(e,t,n){Ve("error",e,t,n)}function Ve(e,t,n,a){const r=Gt(e,t,n,a);zt[e](`[dashboard][${t}] ${n}`,ze(a)),Wt(r)}function Ee(e,t){const n=zt[e];console[e]=(...a)=>{n(...a),Wt(Gt(t,"console",Kn(a),a.length>1?a.slice(1):a[0]))}}function Gt(e,t,n,a){return{city:Vn(),details:a===void 0?void 0:ze(a),level:e,message:n,scope:t,ts:new Date().toISOString(),url:typeof window>"u"?"":window.location.href}}function Vn(){return typeof window>"u"?"":(new URLSearchParams(window.location.search).get("city")??"").trim()}function Kn(e){if(e.length===0)return"console event";const[t]=e;return typeof t=="string"&&t.trim()!==""?t:t instanceof Error?t.message:"console event"}function Wt(e){const t=JSON.stringify(e);if(typeof navigator<"u"&&typeof navigator.sendBeacon=="function"){const n=new Blob([t],{type:"application/json"});if(navigator.sendBeacon("/__client-log",n))return}fetch("/__client-log",{body:t,credentials:"same-origin",headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})}function ze(e,t=0,n=new WeakSet){if(e==null)return e??null;if(typeof e=="string")return e.length>2e3?`${e.slice(0,1999)}…`:e;if(typeof e=="number"||typeof e=="boolean")return e;if(e instanceof Error)return{message:e.message,name:e.name,stack:e.stack};if(typeof e=="function")return`[function ${e.name||"anonymous"}]`;if(t>=4)return"[max-depth]";if(Array.isArray(e))return e.slice(0,20).map(a=>ze(a,t+1,n));if(typeof e=="object"){if(n.has(e))return"[circular]";n.add(e);const a={};for(const[r,i]of Object.entries(e).slice(0,40))a[r]=ze(i,t+1,n);return a}return String(e)}const ut=["cities","status","supervisor","crew","issues","mail","convoys","activity","admin","options"];let Ge=Jt(window.location.search),ft=[];const De=new Set(ut);function Qn(){return Ge}function pt(){return Ge=Jt(window.location.search),Ge}function ae(...e){e.forEach(t=>De.add(t))}function yt(){ae(...ut)}function Xn(e=!1){if(e)return De.clear(),new Set(ut);const t=new Set(De);return De.clear(),t}function Yn(e){ft=e.map(t=>({error:t.error,name:t.name,path:t.path,phasesCompleted:[...t.phasesCompleted??[]],running:t.running,status:t.status}))}function Ft(){return ft.map(e=>({error:e.error,name:e.name,path:e.path,phasesCompleted:[...e.phasesCompleted],running:e.running,status:e.status}))}function Ht(){const e=Ge;if(e==="")return{kind:"supervisor"};const t=ft.find(n=>n.name===e);return t?t.running?{kind:"running",city:t}:{kind:"not-running",city:t}:{kind:"unknown",name:e}}function Zn(e){if(e){if(e.startsWith("session.")||e.startsWith("agent.")){ae("status","crew","options");return}if(e.startsWith("bead.")){ae("status","issues","convoys","admin","options");return}if(e.startsWith("mail.")){ae("status","mail","options");return}if(e.startsWith("convoy.")){ae("status","convoys");return}if(e.startsWith("city.")){ae("cities","status","supervisor");return}if(e.startsWith("service.")||e.startsWith("provider.")||e.startsWith("rig.")){ae("admin");return}}}function Jt(e){return(new URLSearchParams(e).get("city")??"").trim()}function Vt(){const e=document.querySelector('meta[name="supervisor-url"]');return((e==null?void 0:e.content)??"").replace(/\/+$/,"")}function S(){return Qn()}const g=$n({baseUrl:Vt(),headers:{"X-GC-Request":"true"}});ce.setConfig({baseUrl:Vt(),headers:{"X-GC-Request":"true"}});g.use({async onError({error:e,request:t,schemaPath:n}){return ie("api","Request failed",{error:e,method:t.method,schemaPath:n,url:t.url}),e instanceof Error?e:new Error(String(e))},async onRequest({params:e,request:t,schemaPath:n}){be("api","Request start",{method:t.method,params:e,schemaPath:n,url:t.url})},async onResponse({request:e,response:t,schemaPath:n}){const a={method:e.method,ok:t.ok,schemaPath:n,status:t.status,url:e.url};if(!t.ok||t.status>=400){Je("api","Request response",a);return}be("api","Request response",a)}});function s(e,t={},n=[]){const a=document.createElement(e);for(const[r,i]of Object.entries(t))i===void 0||i===!1||(i===!0?a.setAttribute(r,""):a.setAttribute(r,String(i)));for(const r of n)r!=null&&a.append(typeof r=="string"?document.createTextNode(r):r);return a}function k(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function c(e){return document.getElementById(e)}async function ea(){const e=c("city-tabs");if(!e)return;const{data:t,error:n}=await g.GET("/v0/cities");!n&&(t!=null&&t.items)&&Yn(t.items.map(l=>({error:l.error??void 0,name:l.name??"",path:l.path??void 0,phasesCompleted:l.phases_completed??[],running:l.running===!0,status:l.status??void 0})));const a=Ft();if(n||a.length===0)return;const r=S();k(e);const i=s("nav",{class:"city-tabs"}),o=window.location.pathname||"/";i.append(s("a",{href:o,class:`city-tab${r===""?" active":""}`},[s("span",{class:"city-dot running"})," Supervisor"]));for(const l of a){const d=l.running,p=l.name===r,f=s("a",{href:`${o}?city=${encodeURIComponent(l.name)}`,class:`city-tab${p?" active":""}${d?"":" stopped"}`},[s("span",{class:`city-dot${d?" running":""}`}),` ${l.name}`]);i.append(f)}e.append(i)}function mt(e,t=new Date){if(!e)return"";const n=new Date(e);if(isNaN(n.getTime()))return"";const a=Math.max(0,t.getTime()-n.getTime()),r=Math.floor(a/1e3);if(r<60)return`${r}s ago`;const i=Math.floor(r/60);if(i<60)return`${i}m ago`;const o=Math.floor(i/60);return o<24?`${o}h ago`:`${Math.floor(o/24)}d ago`}const Kt=300*1e3,ta=600*1e3;function U(e){if(!e)return"—";const t=new Date(e);if(Number.isNaN(t.getTime()))return"—";const n=new Date,a=t.getFullYear()===n.getFullYear()?{month:"short",day:"numeric",hour:"numeric",minute:"2-digit"}:{month:"short",day:"numeric",year:"numeric",hour:"numeric",minute:"2-digit"};return t.toLocaleString(void 0,a)}function qe(e){if(!e)return{display:"unknown",colorClass:"unknown"};const t=new Date(e);if(Number.isNaN(t.getTime()))return{display:"unknown",colorClass:"unknown"};const n=Math.max(0,Date.now()-t.getTime()),a=mt(e).replace(" ago","");return n=3?`${t[t.length-1]} (${t[0]}/${t[1]})`:`${t[0]}/${t[t.length-1]}`}function na(e){return!e||!e.includes("/")?"":e.split("/",1)[0]??""}function aa(e){return e.startsWith("agent.")||e.startsWith("session.")?"agent":e.startsWith("bead.")||e.startsWith("convoy.")||e.startsWith("order.")?"work":e.startsWith("mail.")?"comms":"system"}function sa(e){return{"session.started":"▶","session.ended":"■","session.crashed":"☠","session.suspended":"⏸","session.woke":"▶","agent.message":"💬","agent.output":"📝","agent.tool_call":"🛠","agent.tool_result":"✅","agent.error":"⚠","bead.created":"📿","bead.updated":"📝","bead.closed":"✅","convoy.created":"🚚","convoy.closed":"✅","mail.delivered":"📬","mail.read":"📨"}[e]??"📋"}function ra(e,t,n,a){const r=I(t);switch(e){case"session.started":return`${I(n)} started`;case"session.ended":return`${I(n)} ended`;case"session.crashed":return`${I(n)} crashed`;case"session.suspended":return`${I(n)} suspended`;case"session.woke":return`${I(n)} woke`;case"bead.created":return`${r} created bead ${n??""}`.trim();case"bead.updated":return`${r} updated bead ${n??""}`.trim();case"bead.closed":return`${r} closed bead ${n??""}`.trim();case"mail.delivered":return`${r} delivered mail`;case"mail.read":return`${r} read mail`;case"convoy.created":return`${r} created convoy ${n??""}`.trim();case"convoy.closed":return`${r} closed convoy ${n??""}`.trim();default:return a??n??e}}function Ke(e,t){return e?e.length<=t?e:`${e.slice(0,t-1)}…`:""}function Z(e){return typeof e!="number"||Number.isNaN(e)||e<=0?4:e}function Qt(e){switch(Z(e)){case 1:return"badge-red";case 2:return"badge-orange";case 3:return"badge-yellow";default:return"badge-muted"}}function oe(e){switch((e??"").toLowerCase()){case"open":case"running":case"ready":case"working":return"badge-green";case"in_progress":case"pending":case"stale":case"warning":return"badge-yellow";case"closed":case"stopped":return"badge-muted";case"error":case"failed":case"stuck":return"badge-red";default:return"badge-blue"}}async function ia(){var w,C,b;const e=S(),t=c("status-banner");if(!t)return;if(!e){await oa(t);return}la();const[n,a,r,i]=await Promise.all([g.GET("/v0/city/{cityName}/status",{params:{path:{cityName:e}}}),g.GET("/v0/city/{cityName}/sessions",{params:{path:{cityName:e},query:{state:"active",peek:!0}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{status:"open",limit:500}}}),g.GET("/v0/city/{cityName}/convoys",{params:{path:{cityName:e},query:{limit:200}}})]);if(n.error||!n.data){k(t),t.append(s("div",{class:"banner-error"},[`Status unavailable for ${e}`]));return}const o=((w=a.data)==null?void 0:w.items)??[],l=((C=r.data)==null?void 0:C.items)??[],d=((b=i.data)==null?void 0:b.items)??[];ca(e,o);const p=o.filter(E=>!E.pool||!E.running||!E.last_active?!1:Date.now()-new Date(E.last_active).getTime()>=1800*1e3).length,f=l.filter(E=>E.assignee&&E.status!=="closed").length,u=l.filter(E=>Z(E.priority)<=2).length,y=o.filter(E=>!E.running).length,m=s("div",{class:"summary-stats"},[F(n.data.agents.running,"Agents"),F(n.data.work.in_progress,"Assigned"),F(n.data.work.open,"Beads"),F(d.length,"Convoys"),F(n.data.mail.unread,"Unread")]),h=s("div",{class:"summary-alerts"});K(h,p>0,"alert-red",`${p} stuck`),K(h,f>0,"alert-yellow",`${f} assigned`),K(h,u>0,"alert-red",`${u} P1/P2`),K(h,y>0,"alert-red",`${y} dead`),h.childNodes.length||h.append(s("span",{class:"alert-item alert-green"},["All clear"])),k(t),t.append(m,h)}async function oa(e){var u,y;da();const[t,n]=await Promise.all([g.GET("/health"),g.GET("/v0/cities")]),a=t.data,r=((u=n.data)==null?void 0:u.items)??[],i=(a==null?void 0:a.cities_total)??r.length,o=(a==null?void 0:a.cities_running)??r.filter(m=>m.running===!0).length,l=Math.max(i-o,0),d=r.filter(m=>!!m.error).length;if(k(e),t.error&&n.error){e.append(s("div",{class:"banner-error"},["Supervisor status unavailable"]));return}const p=s("div",{class:"summary-stats"},[F(i,"🏙️ Cities"),F(o,"🟢 Running"),F(l,"⏸ Stopped"),F(ua(a==null?void 0:a.uptime_sec),"⏱ Uptime")]),f=s("div",{class:"summary-alerts"});K(f,i===0,"alert-yellow","No registered cities"),K(f,l>0,"alert-yellow",`${l} ${l===1?"city":"cities"} not running`),K(f,d>0,"alert-red",`${d} ${d===1?"city":"cities"} reporting errors`),K(f,!!(a!=null&&a.startup&&!a.startup.ready),"alert-yellow",`⏳ Startup: ${((y=a==null?void 0:a.startup)==null?void 0:y.phase)||"starting"}`),f.childNodes.length||f.append(s("span",{class:"alert-item alert-green"},["✓ Supervisor ready"])),e.append(p,f)}function F(e,t){return s("div",{class:"stat"},[s("span",{class:"stat-value"},[String(e??0)]),s("span",{class:"stat-label"},[t])])}function K(e,t,n,a){t&&e.append(s("span",{class:`alert-item ${n}`},[a]))}function ca(e,t){const n=c("scope-banner"),a=c("scope-badge"),r=c("scope-status");if(!n||!a||!r)return;const i=t.find(l=>!l.rig&&!l.pool);if(!i){n.classList.remove("attached"),n.classList.add("detached"),a.className="badge badge-muted",a.textContent="Detached",k(r),r.append(V("Scope",e),V("Overseer","none"));return}n.classList.remove("attached","detached"),n.classList.add(i.attached?"attached":"detached"),a.className=`badge ${i.attached?"badge-green":"badge-muted"}`,a.textContent=i.attached?"Attached":"Detached",k(r);const o=i.last_active?Date.now()-new Date(i.last_active).getTime()(e.client??ce).sse.get({url:"/v0/city/{cityName}/events/stream",...e}),pa=e=>(e.client??ce).sse.get({url:"/v0/city/{cityName}/session/{id}/stream",...e}),ya=e=>((e==null?void 0:e.client)??ce).sse.get({url:"/v0/events/stream",...e});let se=0,st=null;function ma(e){st=e}function Xt(e){se=Math.max(0,e),document.body.dataset.pauseRefresh=se>0?"true":"false"}function W(){Xt(se+1)}function P(){const e=se>0;if(Xt(se-1),e&&se===0&&st)try{st()}catch(t){ie("ui","popPause listener threw",{error:String(t)})}}function gt(){return se>0}function At(e,t){const n=c("output-panel"),a=c("output-panel-cmd"),r=c("output-panel-content");!n||!a||!r||(a.textContent=e,r.textContent=t,n.classList.add("open"))}function Yt(){var e;(e=c("output-panel"))==null||e.classList.remove("open")}function v(e,t,n){const a=c("toast-container");if(!a)return;const r=document.createElement("div");r.className=`toast toast-${e}`,r.innerHTML=`${Rt(t)}
${Rt(n)}
`,a.append(r);const i=e==="error"?9e3:5e3;window.requestAnimationFrame(()=>{r.classList.add("show")}),window.setTimeout(()=>{r.classList.remove("show"),window.setTimeout(()=>{r.remove()},300)},i)}function _(e,t,n="Unexpected dashboard error"){const a=t instanceof Error?t.message:n;ie("ui",e,{error:t,fallbackMessage:n,message:a}),v("error",e,a)}function ga(){var e,t;document.addEventListener("click",n=>{const a=n.target,r=a==null?void 0:a.closest(".collapse-btn");if(r){const p=r.closest(".panel");p==null||p.classList.toggle("collapsed");return}const i=a==null?void 0:a.closest(".expand-btn");if(!i)return;const o=i.closest(".panel");if(!o)return;const l=o.classList.contains("expanded"),d=!!document.querySelector(".panel.expanded");if(document.querySelectorAll(".panel.expanded").forEach(p=>{p.classList.remove("expanded");const f=p.querySelector(".expand-btn");f&&(f.textContent="Expand")}),l){P();return}o.classList.add("expanded"),i.textContent="✕ Close",d||W()}),document.addEventListener("keydown",n=>{if(n.key!=="Escape")return;const a=document.querySelector(".panel.expanded");if(a){a.classList.remove("expanded");const r=a.querySelector(".expand-btn");r&&(r.textContent="Expand"),P()}}),(e=c("output-close-btn"))==null||e.addEventListener("click",()=>Yt()),(t=c("output-copy-btn"))==null||t.addEventListener("click",async()=>{var a;const n=((a=c("output-panel-content"))==null?void 0:a.textContent)??"";try{await navigator.clipboard.writeText(n),v("success","Copied","Output copied to clipboard")}catch{v("error","Copy failed","Clipboard write was rejected")}})}function Rt(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function Zt(e){return typeof e=="object"&&e!==null}function en(e){return Zt(e)&&typeof e.timestamp=="string"}function tn(e){return Zt(e)&&typeof e.actor=="string"&&typeof e.seq=="number"&&typeof e.ts=="string"&&typeof e.type=="string"}function ba(e){return tn(e)}function ha(e){return tn(e)&&typeof e.city=="string"}const Ot=[1e3,2e3,4e3,8e3,15e3],va=15e3;function nn(e){return e{var o;let r=0,i=!1;for(;!n.signal.aborted;){try{const{stream:d}=await ya({client:ce,signal:n.signal,onSseEvent:p=>{var u;r=0,i=!1,(u=t==null?void 0:t.onStatus)==null||u.call(t,"live");const f=p.event??"tagged_event";if(f==="heartbeat"){if(!en(p.data)){_("Invalid supervisor heartbeat frame",p);return}e({event:"heartbeat",id:p.id,data:p.data});return}if(f==="tagged_event"){if(!ha(p.data)){_("Invalid supervisor event frame",p);return}e({event:"tagged_event",id:p.id,data:p.data});return}_(`Unexpected supervisor SSE event: ${f}`,p)}});for await(const p of d);if(n.signal.aborted)break}catch(d){if(n.signal.aborted)return;i||(_("Supervisor event stream failed",d),i=!0)}(o=t==null?void 0:t.onStatus)==null||o.call(t,"reconnecting");const l=nn(r);r+=1,await an(l,n.signal)}})(),{close:()=>n.abort()}}function Sa(e,t,n){var r;const a=new AbortController;return(r=n==null?void 0:n.onStatus)==null||r.call(n,"connecting"),(async()=>{var l;let i=0,o=!1;for(;!a.signal.aborted;){try{const{stream:p}=await fa({client:ce,path:{cityName:e},signal:a.signal,onSseEvent:f=>{var m;i=0,o=!1,(m=n==null?void 0:n.onStatus)==null||m.call(n,"live");const u=f.event??"event",y=f.id!==void 0?String(f.id):void 0;if(u==="heartbeat"){if(!en(f.data)){_("Invalid city heartbeat frame",f);return}t({event:"heartbeat",id:y,data:f.data});return}if(u==="event"){if(!ba(f.data)){_("Invalid city event frame",f);return}t({event:"event",id:y,data:f.data});return}_(`Unexpected city SSE event: ${u}`,f)}});for await(const f of p);if(a.signal.aborted)break}catch(p){if(a.signal.aborted)return;o||(_("City event stream failed",p),o=!0)}(l=n==null?void 0:n.onStatus)==null||l.call(n,"reconnecting");const d=nn(i);i+=1,await an(d,a.signal)}})(),{close:()=>a.abort()}}async function an(e,t){if(!t.aborted)return new Promise(n=>{const a=setTimeout(()=>{t.removeEventListener("abort",r),n()},e),r=()=>{clearTimeout(a),t.removeEventListener("abort",r),n()};t.addEventListener("abort",r)})}function Ca(e,t,n){const a=new AbortController;return(async()=>{try{const{stream:r}=await pa({client:ce,path:{cityName:e,id:t},signal:a.signal,onSseEvent:i=>{if(i.data===void 0){_("Session frame missing data",i);return}n({id:i.id!==void 0?String(i.id):void 0,type:i.event??"message",data:i.data})}});for await(const i of r);}catch(r){a.signal.aborted||_("Session stream failed",r)}})(),{close:()=>a.abort()}}function Ea(e){return e.event==="heartbeat"?"heartbeat":e.data.type}let xe=null,me="",Q="",_e=0;async function ka(){const e=S();if(!e){Na();return}const t=c("crew-loading"),n=c("crew-table"),a=c("crew-empty"),r=c("crew-tbody"),i=c("rigged-body"),o=c("pooled-body");if(!t||!n||!a||!r||!i||!o)return;rt("No crew configured"),t.style.display="block",n.style.display="none",a.style.display="none",k(r);const{data:l,error:d}=await g.GET("/v0/city/{cityName}/sessions",{params:{path:{cityName:e},query:{state:"active",peek:!0}}});if(d||!(l!=null&&l.items)){t.textContent="Failed to load crew",he(i,"No rigged agents"),he(o,"No pooled agents");return}const p=l.items,f=await Promise.all(p.map(async m=>{var w;return!!((w=(await g.GET("/v0/city/{cityName}/session/{id}/pending",{params:{path:{cityName:e,id:m.id}}})).data)!=null&&w.pending)})),u=new Map;await Promise.all(p.map(async m=>{var w;if(!m.active_bead||u.has(m.active_bead))return;const h=await g.GET("/v0/city/{cityName}/bead/{id}",{params:{path:{cityName:e,id:m.active_bead}}});u.set(m.active_bead,(w=h.data)!=null&&w.id?h.data.title??h.data.id:m.active_bead)}));const y=p;y.forEach((m,h)=>{const w=$a(m,f[h]??!1),C=m.active_bead?Ke(u.get(m.active_bead)??m.active_bead,24):"—",b=s("tr",{},[s("td",{},[m.template]),s("td",{},[m.rig??"city"]),s("td",{},[s("span",{class:`badge ${oe(w)}`},[w])]),s("td",{},[C]),s("td",{class:qe(m.last_active).colorClass?`activity-${qe(m.last_active).colorClass}`:""},[s("span",{class:"activity-dot"}),` ${qe(m.last_active).display}`]),s("td",{},[s("span",{class:`badge ${m.attached?"badge-green":"badge-muted"}`},[m.attached?"Attached":"Detached"])]),s("td",{},[La(m.template)," ",sn(m.id,m.template)])]);r.append(b)}),c("crew-count").textContent=String(y.length),t.style.display="none",y.length>0?n.style.display="table":(rt("No crew configured"),a.style.display="block"),xa(p,u),Ta(p)}function Na(){const e=c("crew-loading"),t=c("crew-table"),n=c("crew-empty"),a=c("crew-tbody"),r=c("rigged-body"),i=c("pooled-body");!e||!t||!n||!a||!r||!i||(Pe(),c("crew-count").textContent="0",c("rigged-count").textContent="0",c("pooled-count").textContent="0",e.style.display="none",t.style.display="none",n.style.display="block",rt("Select a city to view crew"),k(a),he(r,"Select a city to view rigged agents"),he(i,"Select a city to view pooled agents"))}function rt(e){var t,n;(n=(t=c("crew-empty"))==null?void 0:t.querySelector("p"))==null||n.replaceChildren(document.createTextNode(e))}function $a(e,t){return t?"questions":e.active_bead?"spinning":e.running?"idle":"finished"}function La(e){const t=s("button",{class:"attach-btn",type:"button"},["📎 Attach"]);return t.addEventListener("click",async()=>{const n=`gc agent attach ${e}`;try{await navigator.clipboard.writeText(n),v("success","Attach command copied",n)}catch{v("error","Copy failed",n)}}),t}function sn(e,t){const n=s("button",{class:"agent-log-link",type:"button","data-session-id":e},[t]);return n.addEventListener("click",()=>{Ra(e,t)}),n}function xa(e,t){const n=c("rigged-body"),a=c("rigged-count");if(!n||!a)return;const r=e.filter(o=>o.rig&&o.pool);if(a.textContent=String(r.length),r.length===0){he(n,"No rigged agents");return}const i=s("tbody");r.forEach(o=>{const l=qe(o.last_active),d=o.active_bead?l.colorClass==="red"?"Stuck":l.colorClass==="yellow"?"Stale":"Working":"Idle";i.append(s("tr",{class:`rigged-${d.toLowerCase()}`},[s("td",{},[sn(o.id,o.template)]),s("td",{},[s("span",{class:"badge badge-muted"},[o.pool??"pool"])]),s("td",{},[o.rig??"city"]),s("td",{class:"rigged-issue"},[o.active_bead?`${o.active_bead} ${t.get(o.active_bead)??""}`.trim():"—"]),s("td",{},[s("span",{class:`badge ${oe(d)}`},[d])]),s("td",{class:`activity-${l.colorClass}`},[s("span",{class:"activity-dot"}),` ${l.display}`])]))}),k(n),n.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Agent"]),s("th",{},["Pool"]),s("th",{},["Rig"]),s("th",{},["Working On"]),s("th",{},["Status"]),s("th",{},["Activity"])])]),i]))}function Ta(e){const t=c("pooled-body"),n=c("pooled-count");if(!t||!n)return;const a=e.filter(i=>!i.rig&&i.pool);if(n.textContent=String(a.length),a.length===0){he(t,"No pooled agents");return}const r=s("tbody");a.forEach(i=>{r.append(s("tr",{},[s("td",{},[i.template]),s("td",{},[s("span",{class:`badge ${i.active_bead?"badge-yellow":"badge-green"}`},[i.active_bead?"Working":"Idle"])]),s("td",{class:"status-hint"},[Ke(i.last_output,80)||"—"]),s("td",{},[U(i.last_active)])]))}),k(t),t.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Agent"]),s("th",{},["State"]),s("th",{},["Work"]),s("th",{},["Activity"])])]),r]))}function he(e,t){k(e),e.append(s("div",{class:"empty-state"},[s("p",{},[t])]))}function Aa(){var e,t;(e=c("log-drawer-close-btn"))==null||e.addEventListener("click",()=>Pe()),(t=c("log-drawer-older-btn"))==null||t.addEventListener("click",()=>{be("crew","Load older transcript clicked",{hasCursor:Q!=="",sessionID:me}),!(!me||!Q)&&on(me,!0)})}async function Ra(e,t){const n=c("agent-log-drawer"),a=c("log-drawer-agent-name"),r=c("log-drawer-messages"),i=c("log-drawer-loading");if(!n||!a||!r||!i)return;if(me===e&&n.style.display!=="none"){Pe();return}Pe(),me=e,Q="",_e=0,a.textContent=t,k(r),r.append(i),i.style.display="block",n.style.display="block",W(),await on(e,!1);const o=S();o&&(xe=Ca(o,e,l=>Oa(l)))}function Pe(){xe==null||xe.close(),xe=null,me="",Q="";const e=c("agent-log-drawer");e&&e.style.display!=="none"&&(e.style.display="none",P())}function rn(){Pe()}async function on(e,t){var p,f,u,y,m;const n=S(),a=c("log-drawer-messages"),r=c("log-drawer-loading"),i=c("log-drawer-older-btn"),o=c("log-drawer-count");if(!n||!a||!r||!i||!o)return;r.style.display="block";const l=await g.GET("/v0/city/{cityName}/session/{id}/transcript",{params:{path:{cityName:n,id:e},query:{tail:String(t?50:25),before:t?Q:void 0}}});if(r.style.display="none",l.error||!l.data){v("error","Transcript failed",((p=l.error)==null?void 0:p.detail)??"Could not load transcript");return}const d=document.createDocumentFragment();for(const h of l.data.turns??[])d.append(cn(h.role,h.text,h.timestamp)),_e+=1;t?a.prepend(d):(k(a),a.append(d)),a.append(r),r.style.display="none",o.textContent=String(_e),Q=((f=l.data.pagination)==null?void 0:f.truncated_before_message)??"",i.style.display=(u=l.data.pagination)!=null&&u.has_older_messages&&Q?"inline-flex":"none",be("crew","Transcript loaded",{hasOlderMessages:((y=l.data.pagination)==null?void 0:y.has_older_messages)??!1,nextBeforeCursor:Q,prepend:t,sessionID:e,turnCount:((m=l.data.turns)==null?void 0:m.length)??0})}function Oa(e){var r;const t=c("log-drawer-messages");if(!t)return;const n=e.data;if(e.type!=="message"||!((r=n==null?void 0:n.data)!=null&&r.message))return;t.append(cn(n.data.message.role??"agent",n.data.message.text??"",n.data.message.timestamp)),_e+=1,c("log-drawer-count").textContent=String(_e);const a=c("log-drawer-body");a&&(a.scrollTop=a.scrollHeight)}function cn(e,t,n){return s("div",{class:"log-msg"},[s("div",{class:"log-msg-header"},[s("span",{class:`log-msg-type log-msg-type-${qa(e)}`},[e]),s("span",{class:"log-msg-time"},[U(n)])]),s("div",{class:"log-msg-body"},[t])])}function qa(e){switch((e??"").toLowerCase()){case"assistant":case"agent":return"assistant";case"system":return"system";case"result":return"result";default:return"user"}}const _a=3e4,it=new Map,Te=new Map;async function Qe(e=!1){const t=S(),n=Date.now(),a=it.get(t);if(!e&&a&&n-a.fetchedAt<_a)return a;const r=Te.get(t);if(r)return r;const i=Pa(t).then(o=>(it.set(t,o),Te.delete(t),o)).catch(o=>{throw Te.delete(t),o});return Te.set(t,i),i}async function Pa(e){var l,d,p,f,u,y,m,h,w,C,b,E;const t={agents:[],rigs:[],sessions:[],beads:[],mail:[],fetchedAt:Date.now()};if(!e)return t;const[n,a,r,i]=await Promise.all([g.GET("/v0/city/{cityName}/config",{params:{path:{cityName:e}}}),g.GET("/v0/city/{cityName}/rigs",{params:{path:{cityName:e}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{status:"open"}}}),g.GET("/v0/city/{cityName}/mail",{params:{path:{cityName:e}}})]);n.error&&Je("options","Config options request failed",{city:e,detail:n.error.detail??null});const o=(((l=n.data)==null?void 0:l.agents)??[]).map(N=>({id:N.name??"",label:N.name??"",recipient:N.name??""})).filter(N=>N.recipient!=="");return be("options","Fetched options",{agentOptions:o.map(N=>N.recipient),beads:((p=(d=r.data)==null?void 0:d.items)==null?void 0:p.length)??0,city:e,configAgents:((u=(f=n.data)==null?void 0:f.agents)==null?void 0:u.length)??0,mail:((m=(y=i.data)==null?void 0:y.items)==null?void 0:m.length)??0,rigs:((w=(h=a.data)==null?void 0:h.items)==null?void 0:w.length)??0}),{agents:[...new Set(o.map(N=>N.recipient))].sort(),rigs:(((C=a.data)==null?void 0:C.items)??[]).map(N=>N.name??"").filter(Boolean),sessions:o,beads:(((b=r.data)==null?void 0:b.items)??[]).map(N=>({id:N.id??"",title:N.title??""})),mail:(((E=i.data)==null?void 0:E.items)??[]).map(N=>({id:N.id??"",subject:N.subject??""})),fetchedAt:Date.now()}}function ja(){it.clear(),Te.clear()}let Ae=null,Re=null;function Ia(){var e,t,n,a,r,i,o,l,d,p;(e=c("action-modal-close-btn"))==null||e.addEventListener("click",()=>ke(null)),(t=c("action-modal-cancel-btn"))==null||t.addEventListener("click",()=>ke(null)),(a=(n=c("action-modal"))==null?void 0:n.querySelector(".modal-backdrop"))==null||a.addEventListener("click",()=>ke(null)),(r=c("action-form"))==null||r.addEventListener("submit",f=>{var h,w,C;f.preventDefault();const u=((h=c("action-bead-id"))==null?void 0:h.value.trim())??"",y=((w=c("action-target"))==null?void 0:w.value.trim())??"",m=((C=c("action-rig"))==null?void 0:C.value.trim())??"";!u||!y||ke({beadID:u,rig:m,target:y})}),(i=c("confirm-modal-close-btn"))==null||i.addEventListener("click",()=>Ne(!1)),(o=c("confirm-modal-cancel-btn"))==null||o.addEventListener("click",()=>Ne(!1)),(l=c("confirm-modal-confirm-btn"))==null||l.addEventListener("click",()=>Ne(!0)),(p=(d=c("confirm-modal"))==null?void 0:d.querySelector(".modal-backdrop"))==null||p.addEventListener("click",()=>Ne(!1)),document.addEventListener("keydown",f=>{if(f.key==="Escape"){if(ve("action-modal")){ke(null);return}ve("confirm-modal")&&Ne(!1)}})}async function bt(e){const t=c("action-modal"),n=c("action-form"),a=c("action-modal-title"),r=c("action-modal-submit-btn"),i=c("action-bead-group"),o=c("action-bead-id"),l=c("action-bead-hint"),d=c("action-target"),p=c("action-target-label"),f=c("action-rig-group"),u=c("action-rig"),y=c("action-modal-help"),m=c("action-target-list"),h=c("action-rig-list");if(!t||!n||!a||!r||!i||!o||!l||!d||!p||!f||!u||!y||!m||!h)return _("Action modal unavailable",new Error("missing action modal DOM")),null;const w=await Qe();return qt(m,w.agents),qt(h,w.rigs),a.textContent=e.title,r.textContent=Ma(e.mode),p.textContent=e.mode==="reassign"?"Assignee":"Target agent or pool",y.textContent=Ua(e.mode),o.value=e.beadID??"",o.readOnly=!!e.beadID,i.classList.toggle("readonly",o.readOnly),l.textContent=e.beadLabel??"",d.value=e.initialTarget??"",u.value=e.initialRig??"",f.hidden=e.mode==="reassign",u.disabled=e.mode==="reassign",ve("action-modal")||W(),t.style.display="flex",window.setTimeout(()=>{if(e.beadID){d.focus();return}o.focus()},0),new Promise(C=>{Ae=C})}async function Ba(e){const t=c("confirm-modal"),n=c("confirm-modal-title"),a=c("confirm-modal-body"),r=c("confirm-modal-confirm-btn");return!t||!n||!a||!r?(_("Confirm modal unavailable",new Error("missing confirm modal DOM")),!1):(n.textContent=e.title,a.textContent=e.body,r.textContent=e.confirmLabel,ve("confirm-modal")||W(),t.style.display="flex",new Promise(i=>{Re=i}))}function qt(e,t){k(e),t.forEach(n=>{e.append(s("option",{value:n}))})}function Ma(e){switch(e){case"assign":return"Assign";case"reassign":return"Reassign";default:return"Sling"}}function Ua(e){switch(e){case"assign":return"Launch a bead directly to a target, with an optional rig override.";case"reassign":return"Pick a new assignee from the active city sessions or type one manually.";default:return"Dispatch this bead to a target, with an optional rig constraint."}}function ke(e){const t=c("action-modal"),n=c("action-form");if(!t||!n)return;const a=ve("action-modal");t.style.display="none",n.reset(),c("action-rig").disabled=!1,c("action-bead-id").readOnly=!1,a&&P(),Ae==null||Ae(e),Ae=null}function Ne(e){const t=c("confirm-modal");if(!t)return;const n=ve("confirm-modal");t.style.display="none",n&&P(),Re==null||Re(e),Re=null}function ve(e){var t;return((t=c(e))==null?void 0:t.style.display)==="flex"}let We=[],ot="ready",we="all",Xe="";async function le(){var o,l,d,p;const e=S(),t=c("issues-list");if(!t)return;if(!e){Da();return}const[n,a,r]=await Promise.all([g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{status:"open",limit:500}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{status:"in_progress",limit:500}}}),Qe(!0)]);if(n.error&&a.error||!((o=n.data)!=null&&o.items)&&!((l=a.data)!=null&&l.items)){k(t),t.append(s("div",{class:"panel-error"},["Could not load beads."]));return}We=[...((d=n.data)==null?void 0:d.items)??[],...((p=a.data)==null?void 0:p.items)??[]].filter(f=>!za(f)).sort((f,u)=>{const y=Z(f.priority),m=Z(u.priority);return y!==m?y-m:(u.created_at??"").localeCompare(f.created_at??"")}),c("issues-count").textContent=String(We.length);const i=c("rig-filter-tabs");i&&(k(i),i.append(ct("all",we==="all")),r.rigs.forEach(f=>i.append(ct(f,we===f)))),ht()}function Da(){const e=c("issues-list"),t=c("rig-filter-tabs"),n=c("issue-detail");if(!e||!t||!n)return;ye();const a=n.style.display==="block";n.style.display="none",e.style.display="block",k(e),e.append(s("div",{class:"empty-state"},[s("p",{},["Select a city to view beads"])])),k(t),we="all",Xe="",We=[],t.append(ct("all",!0)),c("issues-count").textContent="0",a&&P()}function ht(){const e=c("issues-list");if(!e)return;k(e);const t=We.filter(a=>{const r=a.assignee?"progress":"ready",i=ot==="all"||ot===r,o=we==="all"||tt(a)===we;return i&&o});if(t.length===0){e.append(s("div",{class:"empty-state"},[s("p",{},["No beads"])]));return}const n=s("tbody");t.forEach(a=>{const r=s("tr",{class:`issue-row priority-${Z(a.priority)}`,"data-issue-id":a.id??"","data-status":a.assignee?"progress":"ready","data-rig":tt(a)},[s("td",{},[s("span",{class:`badge ${Qt(a.priority)}`},[`P${Z(a.priority)}`])]),s("td",{},[s("span",{class:"issue-id"},[a.id??""])]),s("td",{class:"issue-title"},[Ke(a.title??a.id??"",80)]),s("td",{class:"issue-rig"},[tt(a)]),s("td",{class:"issue-status"},[a.assignee?s("span",{class:"badge badge-blue",title:a.assignee},[a.assignee]):s("span",{class:"badge badge-green"},["Ready"])]),s("td",{class:"issue-age"},[U(a.created_at)]),s("td",{},[es(a.id??"")])]);r.addEventListener("click",i=>{i.target.closest(".sling-btn")||a.id&&de(a.id)}),n.append(r)}),e.append(s("table",{id:"work-table"},[s("thead",{},[s("tr",{},[s("th",{},["Pri"]),s("th",{},["ID"]),s("th",{},["Title"]),s("th",{},["Rig"]),s("th",{},["Status"]),s("th",{},["Age"]),s("th",{},["Actions"])])]),n]))}function ct(e,t){const n=s("button",{class:`rig-btn${t?" active":""}`,"data-rig":e},[e==="all"?"All":e]);return n.addEventListener("click",()=>{we=e,document.querySelectorAll(".rig-btn").forEach(a=>a.classList.remove("active")),n.classList.add("active"),ht()}),n}function tt(e){var t;return((t=e.id)==null?void 0:t.split("-")[0])??"city"}function za(e){return(e.issue_type??"").toLowerCase()==="convoy"?!0:(e.labels??[]).some(t=>t.startsWith("gc:queue")||t.startsWith("gc:message"))}function Ga(){var e,t,n,a,r,i,o;document.querySelectorAll(".tab-btn").forEach(l=>{l.addEventListener("click",d=>{const p=d.currentTarget;ot=p.dataset.tab??"ready",document.querySelectorAll(".tab-btn").forEach(f=>f.classList.remove("active")),p.classList.add("active"),ht()})}),(e=c("new-issue-btn"))==null||e.addEventListener("click",()=>ln()),(t=c("issue-modal-close-btn"))==null||t.addEventListener("click",()=>ye()),(n=c("issue-modal-cancel-btn"))==null||n.addEventListener("click",()=>ye()),(r=(a=c("issue-modal"))==null?void 0:a.querySelector(".modal-backdrop"))==null||r.addEventListener("click",()=>ye()),(i=c("issue-form"))==null||i.addEventListener("submit",l=>{l.preventDefault(),Wa()}),(o=c("issue-back-btn"))==null||o.addEventListener("click",()=>Ka()),document.addEventListener("keydown",l=>{var d;l.key==="Escape"&&((d=c("issue-modal"))==null?void 0:d.style.display)==="block"&&ye()})}function ln(){var t,n,a;if(!S()){v("info","No city selected","Select a city to create a bead");return}const e=c("issue-modal");e&&(e.style.display!=="block"&&W(),e.style.display="block",(n=(t=c("issues-panel"))==null?void 0:t.scrollIntoView)==null||n.call(t,{behavior:"smooth",block:"center"}),(a=c("issue-title"))==null||a.focus())}function ye(){var n;const e=c("issue-modal");if(!e)return;const t=e.style.display==="block";e.style.display="none",(n=c("issue-form"))==null||n.reset(),t&&P()}async function Wa(){var r,i,o;const e=((r=c("issue-title"))==null?void 0:r.value.trim())??"",t=((i=c("issue-description"))==null?void 0:i.value.trim())??"",n=Number(((o=c("issue-priority"))==null?void 0:o.value)??"2");if(!e)return;const a=await ts({title:e,description:t,priority:n});if(!a.ok){v("error","Create failed",a.error??"Could not create issue");return}v("success","Issue created",e),ye(),await le()}async function de(e){var l,d,p;const t=S();if(!t)return;Xe=e,((l=c("issue-detail"))==null?void 0:l.style.display)!=="block"&&W(),c("issues-list").style.display="none",c("issue-detail").style.display="block";const[n,a,r]=await Promise.all([g.GET("/v0/city/{cityName}/bead/{id}",{params:{path:{cityName:t,id:e}}}),g.GET("/v0/city/{cityName}/bead/{id}/deps",{params:{path:{cityName:t,id:e}}}),Qe()]);if(n.error||!n.data){v("error","Issue failed",((d=n.error)==null?void 0:d.detail)??"Could not load bead");return}const i=n.data;c("issue-detail-id").textContent=i.id??e,c("issue-detail-title-text").textContent=i.title??e,c("issue-detail-description").textContent=i.description||"(no description)";const o=c("issue-detail-priority");o.className=`badge ${Qt(i.priority)}`,o.textContent=`P${Z(i.priority)}`,c("issue-detail-status").textContent=i.status??"open",c("issue-detail-status").className=`issue-status ${i.status??"open"}`,c("issue-detail-type").textContent=i.issue_type?`Type: ${i.issue_type}`:"",c("issue-detail-owner").textContent=i.assignee?`Owner: ${i.assignee}`:"Owner: unassigned",c("issue-detail-created").textContent=i.created_at?`Created: ${U(i.created_at)}`:"",Ha(i,r.agents),Fa(((p=a.data)==null?void 0:p.children)??[])}function Fa(e){const t=c("issue-detail-deps"),n=c("issue-detail-depends-on"),a=c("issue-detail-blocks-section"),r=c("issue-detail-blocks");if(!(!t||!n||!a||!r)){if(k(n),k(r),e.length===0){t.style.display="none",a.style.display="none";return}t.style.display="block",e.forEach(i=>{const o=s("span",{class:"issue-dep-item","data-issue-id":i.id??""},[`→ ${i.id??""}`]);o.addEventListener("click",()=>{i.id&&de(i.id)}),n.append(o)}),a.style.display="none"}}function Ha(e,t){const n=c("issue-detail-actions");if(!n||!e.id)return;k(n);const a=s("div",{class:"issue-actions-bar"}),r=e.status==="closed"?nt("↺ Reopen","reopen",()=>void Xa(e.id)):nt("✓ Close","close",()=>void Qa(e.id));a.append(r),e.status!=="closed"&&a.append(nt("🚚 Sling","sling",()=>void dn(e.id)));const i=s("div",{class:"issue-action-group"},[s("label",{class:"issue-action-label"},["Priority"]),Ja(e.id,e.priority)]),o=s("div",{class:"issue-action-group"},[s("label",{class:"issue-action-label"},["Assign"]),Va(e.id,e.assignee,t)]);n.append(a,i,o)}function nt(e,t,n){const a=s("button",{class:`issue-action-btn ${t}`,type:"button"},[e]);return a.addEventListener("click",n),a}function Ja(e,t){const n=s("select",{class:"issue-action-select",id:"issue-action-priority"});return[1,2,3,4].forEach(a=>{const r=s("option",{value:a,selected:Z(t)===a},[`P${a}`]);n.append(r)}),n.addEventListener("change",()=>{Ya(e,Number(n.value))}),n}function Va(e,t,n){const a=s("select",{class:"issue-action-select",id:"issue-action-assignee"});return a.append(s("option",{value:""},["Unassigned"])),n.forEach(r=>{a.append(s("option",{value:r,selected:t===r},[r]))}),a.addEventListener("change",()=>{Za(e,a.value)}),a}function Ka(){const e=c("issue-detail"),t=(e==null?void 0:e.style.display)==="block";e.style.display="none",c("issues-list").style.display="block",Xe="",t&&P()}async function Qa(e){const t=S();if(!t)return;const n=await g.POST("/v0/city/{cityName}/bead/{id}/close",{params:{path:{cityName:t,id:e}}});if(n.error){v("error","Close failed",n.error.detail??"Could not close issue");return}v("success","Closed",e),await le(),await de(e)}async function Xa(e){const t=S();if(!t)return;const n=await g.POST("/v0/city/{cityName}/bead/{id}/reopen",{params:{path:{cityName:t,id:e}}});if(n.error){v("error","Reopen failed",n.error.detail??"Could not reopen issue");return}v("success","Reopened",e),await le(),await de(e)}async function Ya(e,t){const n=S();if(!n)return;const a=await g.POST("/v0/city/{cityName}/bead/{id}/update",{params:{path:{cityName:n,id:e}},body:{priority:t}});if(a.error){v("error","Priority failed",a.error.detail??"Could not update priority");return}v("success","Priority updated",`${e} → P${t}`),await le(),await de(e)}async function Za(e,t){const n=S();if(!n)return;const a=await g.POST("/v0/city/{cityName}/bead/{id}/assign",{params:{path:{cityName:n,id:e}},body:{assignee:t}});if(a.error){v("error","Assign failed",a.error.detail??"Could not update assignee");return}v("success","Assignment updated",t||"Unassigned"),await le(),await de(e)}async function dn(e){const t=S();if(!t)return;const n=await bt({beadID:e,beadLabel:e,mode:"sling",title:"Sling Bead"});if(!n)return;const a=await g.POST("/v0/city/{cityName}/sling",{params:{path:{cityName:t}},body:{bead:e,target:n.target,rig:n.rig||void 0}});if(a.error){v("error","Sling failed",a.error.detail??"Could not sling issue");return}v("success","Work assigned",`${e} → ${n.target}`),await le(),Xe===e&&await de(e)}function es(e){const t=s("button",{class:"sling-btn",type:"button","data-bead-id":e},["Sling"]);return t.addEventListener("click",n=>{n.stopPropagation(),dn(e)}),t}async function ts(e){const t=S();if(!t)return{ok:!1,error:"no city selected"};const{error:n}=await g.POST("/v0/city/{cityName}/beads",{params:{path:{cityName:t}},body:{title:e.title,description:e.description,rig:e.rig,priority:e.priority,assignee:e.assignee}});return n?{ok:!1,error:n.detail??n.title??"create failed"}:{ok:!0}}let M="inbox",Oe=[],L=null;async function Me(){const e=S(),t=c("mail-loading"),n=c("mail-threads"),a=c("mail-empty"),r=c("mail-all");if(!t||!n||!a||!r)return;if(!e){ns();return}vt("No mail in inbox"),t.style.display="block",n.style.display="none",a.style.display="none";const{data:i,error:o}=await g.GET("/v0/city/{cityName}/mail",{params:{path:{cityName:e},query:{status:"all",limit:200}}});if(t.style.display="none",o||!(i!=null&&i.items)){k(n),n.append(s("div",{class:"panel-error"},["Could not load mail."])),n.style.display="block";return}Oe=[...i.items].sort((l,d)=>(d.created_at??"").localeCompare(l.created_at??"")),c("mail-count").textContent=String(Oe.length),as(Oe),ss(Oe),os()}function ns(){const e=c("mail-loading"),t=c("mail-threads"),n=c("mail-empty"),a=c("mail-all");if(!e||!t||!n||!a)return;re()?(D(M),P()):D(M),L=null,Oe=[],c("mail-count").textContent="0",e.style.display="none",k(t),k(a),t.style.display="none",vt("Select a city to view mail"),n.style.display=M==="inbox"?"block":"none",a.append(s("div",{class:"empty-state"},[s("p",{},["Select a city to view mail traffic"])]))}function vt(e){var t,n;(n=(t=c("mail-empty"))==null?void 0:t.querySelector("p"))==null||n.replaceChildren(document.createTextNode(e))}function as(e){const t=c("mail-threads"),n=c("mail-empty");if(!t||!n)return;const a=ys(e);if(k(t),a.length===0){t.style.display="none",vt("No mail in inbox"),n.style.display="block";return}n.style.display="none",a.forEach(r=>{const i=r.messages[r.messages.length-1],o=(i.body??"").trim().slice(0,60),l=s("div",{class:`mail-thread${r.unreadCount>0?" mail-thread-unread":""}`},[s("div",{class:"mail-thread-header"},[s("div",{class:"mail-thread-left"},[s("span",{class:"mail-from"},[I(i.from)])]),s("div",{class:"mail-thread-center"},[s("span",{class:"mail-subject"},[r.subject||"(no subject)"]),o?s("span",{class:"mail-thread-preview"},[` — ${o}`]):null]),s("div",{class:"mail-thread-right"},[s("span",{class:"mail-time"},[mt(i.created_at)]),r.unreadCount>0?s("span",{class:"badge badge-unread"},[`${r.unreadCount} unread`]):null])])]);l.addEventListener("click",()=>{rs(r.id)}),t.append(l)}),t.style.display=M==="inbox"?"block":"none"}function ss(e){const t=c("mail-all");if(!t)return;if(k(t),e.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No mail traffic"])]));return}const n=s("tbody");e.forEach(a=>{const r=s("tr",{class:`mail-row${a.read?"":" mail-unread"}`},[s("td",{class:"mail-from"},[I(a.from)]),s("td",{class:"mail-to"},[I(a.to)]),s("td",{},[s("span",{class:"mail-subject"},[a.subject??"(no subject)"])]),s("td",{class:"mail-time"},[U(a.created_at)])]);r.addEventListener("click",()=>{a.id&&is(a.id)}),n.append(r)}),t.append(s("table",{class:"mail-all-table"},[s("thead",{},[s("tr",{},[s("th",{},["From"]),s("th",{},["To"]),s("th",{},["Subject"]),s("th",{},["Time"])])]),n])),t.style.display=M==="all"?"block":"none"}async function rs(e){var i,o;const t=S();if(!t)return;const n=await g.GET("/v0/city/{cityName}/mail/thread/{id}",{params:{path:{cityName:t,id:e}}});if(n.error||!((i=n.data)!=null&&i.items)||n.data.items.length===0){v("error","Thread failed",((o=n.error)==null?void 0:o.detail)??"Could not load mail thread");return}const a=n.data.items,r=a[a.length-1]??a[0];L=r,un(r,a)}async function is(e){var a;const t=S();if(!t)return;const n=await g.GET("/v0/city/{cityName}/mail/{id}",{params:{path:{cityName:t,id:e}}});if(n.error||!n.data){v("error","Message failed",((a=n.error)==null?void 0:a.detail)??"Could not load message");return}L=n.data,await g.POST("/v0/city/{cityName}/mail/{id}/read",{params:{path:{cityName:t,id:e}}}),L.read=!0,un(L,[L]),Me()}function un(e,t){const n=re();c("mail-detail-subject").textContent=e.subject??"(no subject)",c("mail-detail-from").textContent=I(e.from),c("mail-detail-time").textContent=U(e.created_at);const a=c("mail-detail-body");a&&(k(a),t.forEach((r,i)=>{i>0&&a.append(s("hr")),a.append(s("div",{class:"mail-thread-msg-header"},[s("span",{class:"mail-from"},[I(r.from)]),s("span",{class:"mail-time"},[U(r.created_at)])]),s("div",{class:"mail-thread-msg-subject"},[r.subject??"(no subject)"]),s("pre",{},[r.body??""]))})),fn(),D("detail"),pn("mail-detail"),n||W()}function D(e){const t=c("mail-list"),n=c("mail-all"),a=c("mail-detail"),r=c("mail-compose");!t||!n||!a||!r||(t.style.display=e==="inbox"?"block":"none",n.style.display=e==="all"?"block":"none",a.style.display=e==="detail"?"block":"none",r.style.display=e==="compose"?"block":"none")}function os(){var e,t;((e=c("mail-compose"))==null?void 0:e.style.display)==="block"||((t=c("mail-detail"))==null?void 0:t.style.display)==="block"||D(M)}function cs(){var e,t,n,a,r,i,o,l;document.querySelectorAll(".mail-tab").forEach(d=>{d.addEventListener("click",p=>{const f=p.currentTarget;M=f.dataset.tab??"inbox",document.querySelectorAll(".mail-tab").forEach(u=>u.classList.remove("active")),f.classList.add("active"),D(M)})}),(e=c("mail-back-btn"))==null||e.addEventListener("click",()=>{const d=re();D(M),L=null,d&&P()}),(t=c("compose-mail-btn"))==null||t.addEventListener("click",()=>{lt()}),(n=c("compose-back-btn"))==null||n.addEventListener("click",()=>{const d=!!L,p=re();D(d?"detail":M),p&&!d&&P()}),(a=c("compose-cancel-btn"))==null||a.addEventListener("click",()=>{const d=re();D(M),d&&P()}),(r=c("mail-reply-btn"))==null||r.addEventListener("click",()=>{L!=null&&L.id&<(L)}),(i=c("mail-send-btn"))==null||i.addEventListener("click",()=>{ls()}),(o=c("mail-archive-btn"))==null||o.addEventListener("click",()=>{L!=null&&L.id&&ds(L.id)}),(l=c("mail-toggle-unread-btn"))==null||l.addEventListener("click",()=>{L!=null&&L.id&&us(L)})}async function lt(e){if(!S()){v("info","No city selected","Select a city to compose mail"),Je("mail","Compose blocked without city",{replyTo:(e==null?void 0:e.id)??null});return}const t=c("compose-to");if(!t)return;const n=re();k(t),t.append(s("option",{value:""},["Select recipient…"]));try{const a=await Qe();a.sessions.forEach(r=>{t.append(s("option",{value:r.recipient},[r.label]))}),H("mail","Compose options loaded",{city:S(),recipients:a.sessions.length,replyTo:(e==null?void 0:e.id)??null})}catch(a){ie("mail","Compose options failed",{city:S(),error:a}),_("Mail options failed",a,"Could not load recipients")}c("compose-subject").value=e?fs(e.subject??""):"",c("compose-body").value="",c("compose-reply-to").value=(e==null?void 0:e.id)??"",c("mail-compose-title").textContent=e?"Reply":"New Message",e!=null&&e.from&&(ps(t,e.from),t.value=e.from),D("compose"),pn("compose-subject"),H("mail","Compose form opened",{city:S(),replyTo:(e==null?void 0:e.id)??null,selectedRecipient:t.value||null}),n||W()}async function ls(){var l,d,p,f;const e=S();if(!e)return;const t=((l=c("compose-to"))==null?void 0:l.value)??"",n=((d=c("compose-subject"))==null?void 0:d.value.trim())??"",a=((p=c("compose-body"))==null?void 0:p.value)??"",r=((f=c("compose-reply-to"))==null?void 0:f.value)??"";if(!t||!n){v("error","Missing fields","Recipient and subject are required"),Je("mail","Send blocked by missing fields",{bodyLength:a.length,city:e,subject:n,to:t});return}H("mail","Send requested",{bodyLength:a.length,city:e,replyTo:r||null,subject:n,to:t});const i=r?await g.POST("/v0/city/{cityName}/mail/{id}/reply",{params:{path:{cityName:e,id:r}},body:{body:a,subject:n}}):await g.POST("/v0/city/{cityName}/mail",{params:{path:{cityName:e}},body:{to:t,subject:n,body:a,from:"dashboard"}});if(i.error){ie("mail","Send failed",{bodyLength:a.length,city:e,error:i.error,replyTo:r||null,subject:n,to:t}),v("error","Send failed",i.error.detail??"Could not send message");return}H("mail","Send succeeded",{bodyLength:a.length,city:e,replyTo:r||null,subject:n,to:t}),v("success","Message sent",n);const o=re();D("inbox"),L=null,o&&P(),await Me()}async function ds(e){var r;const t=S();if(!t)return;const n=await g.POST("/v0/city/{cityName}/mail/{id}/archive",{params:{path:{cityName:t,id:e}}});if(n.error){v("error","Archive failed",n.error.detail??"Could not archive message");return}v("success","Archived",e);const a=((r=c("mail-detail"))==null?void 0:r.style.display)==="block";D(M),L=null,a&&P(),await Me()}async function us(e){const t=S();if(!t||!e.id)return;const n=e.read?"/v0/city/{cityName}/mail/{id}/mark-unread":"/v0/city/{cityName}/mail/{id}/read",a=await g.POST(n,{params:{path:{cityName:t,id:e.id}}});if(a.error){v("error","Update failed",a.error.detail??"Could not update message");return}e.read=!e.read,L={...e},fn(),v("success","Updated",e.subject??e.id),await Me()}function fn(){const e=c("mail-toggle-unread-btn");e&&(e.textContent=L!=null&&L.read?"Mark unread":"Mark read")}function re(){var e,t;return((e=c("mail-detail"))==null?void 0:e.style.display)==="block"||((t=c("mail-compose"))==null?void 0:t.style.display)==="block"}function fs(e){return e?e.toLowerCase().startsWith("re:")?e:`Re: ${e}`:"Re:"}function ps(e,t){!t||[...e.options].some(n=>n.value===t)||e.append(s("option",{value:t},[t]))}function pn(e){var t,n;(n=(t=c("mail-panel"))==null?void 0:t.scrollIntoView)==null||n.call(t,{behavior:"smooth",block:"center"}),window.setTimeout(()=>{var a;(a=c(e))==null||a.focus()},0)}function ys(e){const t=new Map;e.forEach(i=>{i.id&&t.set(i.id,i)});function n(i){let o=i;const l=new Set;for(;o.reply_to&&o.id&&!l.has(o.id);){l.add(o.id);const d=t.get(o.reply_to);if(!d)break;o=d}return o.thread_id??o.id??Math.random().toString(36)}const a=new Map;e.forEach(i=>{const o=n(i),l=a.get(o)??{id:o,messages:[],subject:i.subject??"",unreadCount:0};l.messages.push(i),i.read||(l.unreadCount+=1),!l.subject&&i.subject&&(l.subject=i.subject),a.set(o,l)});const r=[...a.values()];return r.forEach(i=>{i.messages.sort((o,l)=>(o.created_at??"").localeCompare(l.created_at??""))}),r.sort((i,o)=>{var p,f;const l=((p=i.messages[i.messages.length-1])==null?void 0:p.created_at)??"";return(((f=o.messages[o.messages.length-1])==null?void 0:f.created_at)??"").localeCompare(l)}),r}let ge="";async function wt(){var o;const e=S(),t=c("convoy-list");if(!t)return;if(!e){ms();return}const n=await g.GET("/v0/city/{cityName}/convoys",{params:{path:{cityName:e},query:{limit:200}}});if(n.error||!((o=n.data)!=null&&o.items)){k(t),t.append(s("div",{class:"panel-error"},["Could not load convoys."]));return}const r=(await Promise.all(n.data.items.map(async l=>gs(e,l.id??"")))).filter(l=>l!==null);if(c("convoy-count").textContent=String(r.length),k(t),r.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No active convoys"])]));return}const i=s("tbody");r.forEach(l=>{const d=s("tr",{class:"convoy-row","data-convoy-id":l.id},[s("td",{},[s("span",{class:`badge ${oe(yn(l))}`},[bs(l)])]),s("td",{},[s("span",{class:"convoy-id"},[l.id]),l.title?s("div",{class:"convoy-title"},[l.title]):null,l.assignees.length?s("div",{class:"convoy-assignees"},l.assignees.map(p=>s("span",{class:"assignee-chip"},[p]))):null]),s("td",{class:"convoy-progress-cell"},[s("div",{class:"convoy-progress-header"},[s("span",{class:"convoy-progress-fraction"},[`${l.closed}/${l.total}`]),l.total>0?s("span",{class:"convoy-progress-pct"},[`${l.progressPct}%`]):null]),l.total>0?s("div",{class:"progress-bar"},[s("div",{class:"progress-fill",style:`width: ${l.progressPct}%;`})]):null]),s("td",{class:"convoy-work-cell"},[s("div",{class:"convoy-work-breakdown"},[l.ready>0?s("span",{class:"work-chip work-ready"},[`${l.ready} ready`]):null,l.inProgress>0?s("span",{class:"work-chip work-inprogress"},[`${l.inProgress} active`]):null,l.closed===l.total&&l.total>0?s("span",{class:"work-chip work-done"},["all done"]):null])]),s("td",{class:`activity-${l.lastActivity.colorClass}`},[s("span",{class:"activity-dot"}),` ${l.lastActivity.display}`])]);d.addEventListener("click",()=>{gn(l.id)}),i.append(d)}),t.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Status"]),s("th",{},["Convoy"]),s("th",{},["Progress"]),s("th",{},["Work"]),s("th",{},["Activity"])])]),i]))}function ms(){const e=c("convoy-list"),t=c("convoy-detail"),n=c("convoy-create-form");if(!e||!t||!n)return;const a=t.style.display==="block"||n.style.display==="block";ge="",c("convoy-count").textContent="0",t.style.display="none",n.style.display="none",c("convoy-add-issue-form").style.display="none",e.style.display="block",k(e),e.append(s("div",{class:"empty-state"},[s("p",{},["Select a city to view convoys"])])),a&&P()}async function gs(e,t){var f,u,y,m;if(!t)return null;const n=await g.GET("/v0/city/{cityName}/convoy/{id}",{params:{path:{cityName:e,id:t}}});if(n.error||!n.data)return null;const a=n.data.children??[],r=new Set;let i=0,o=0,l="";a.forEach(h=>{(h.status??"").toLowerCase()!=="closed"&&(h.assignee?(o+=1,r.add(h.assignee)):i+=1),l=[l,h.created_at??""].sort().slice(-1)[0]??l});const d=((f=n.data.progress)==null?void 0:f.total)??a.length,p=((u=n.data.progress)==null?void 0:u.closed)??a.filter(h=>h.status==="closed").length;return{id:t,title:((y=n.data.convoy)==null?void 0:y.title)??t,status:(m=n.data.convoy)==null?void 0:m.status,progressPct:d>0?Math.round(p/d*100):0,total:d,closed:p,ready:i,inProgress:o,assignees:[...r].sort(),lastActivity:qe(l)}}function yn(e){return e.total>0&&e.closed===e.total?"done":e.inProgress>0?"active":e.ready>0?"waiting":e.status??"open"}function bs(e){switch(yn(e)){case"done":return"✓ Done";case"active":return"Active";case"waiting":return"Waiting";default:return e.status??"Open"}}function hs(){var e,t,n,a,r,i,o,l;(e=c("new-convoy-btn"))==null||e.addEventListener("click",()=>{mn()}),(t=c("convoy-back-btn"))==null||t.addEventListener("click",()=>vs()),(n=c("convoy-create-back-btn"))==null||n.addEventListener("click",()=>dt()),(a=c("convoy-create-cancel-btn"))==null||a.addEventListener("click",()=>dt()),(r=c("convoy-create-submit-btn"))==null||r.addEventListener("click",()=>{ws()}),(i=c("convoy-add-issue-btn"))==null||i.addEventListener("click",()=>{c("convoy-add-issue-form").style.display="flex"}),(o=c("convoy-add-issue-cancel"))==null||o.addEventListener("click",()=>{c("convoy-add-issue-form").style.display="none"}),(l=c("convoy-add-issue-submit"))==null||l.addEventListener("click",()=>{Ss()})}function mn(){var n;if(!S()){v("info","No city selected","Select a city to create a convoy");return}const e=c("convoy-create-form"),t=(e==null?void 0:e.style.display)==="block";ge="",c("convoy-list").style.display="none",c("convoy-detail").style.display="none",e.style.display="block",c("convoy-create-name").value="",c("convoy-create-issues").value="",t||W(),bn("convoy-create-name"),(n=c("convoy-create-name"))==null||n.focus()}async function gn(e){var l,d,p,f,u,y,m,h;const t=S();if(!t)return;ge=e,((l=c("convoy-detail"))==null?void 0:l.style.display)!=="block"&&W(),c("convoy-list").style.display="none",c("convoy-create-form").style.display="none",c("convoy-detail").style.display="block",bn("convoy-detail"),c("convoy-detail-id").textContent=e,c("convoy-detail-title").textContent=`Convoy: ${e}`,c("convoy-issues-loading").style.display="block",c("convoy-issues-table").style.display="none",c("convoy-issues-empty").style.display="none",c("convoy-add-issue-form").style.display="none";const n=await g.GET("/v0/city/{cityName}/convoy/{id}",{params:{path:{cityName:t,id:e}}});if(c("convoy-issues-loading").style.display="none",n.error||!n.data){c("convoy-issues-empty").style.display="block",c("convoy-issues-empty").querySelector("p").textContent=((d=n.error)==null?void 0:d.detail)??"Failed to load convoy";return}const a=((p=n.data.progress)==null?void 0:p.total)??((f=n.data.children)==null?void 0:f.length)??0,r=((u=n.data.progress)==null?void 0:u.closed)??((y=n.data.children)==null?void 0:y.filter(w=>w.status==="closed").length)??0;c("convoy-detail-status").className=`badge ${oe(((m=n.data.convoy)==null?void 0:m.status)??"open")}`,c("convoy-detail-status").textContent=((h=n.data.convoy)==null?void 0:h.status)??"open",c("convoy-detail-progress").textContent=`${r}/${a}`;const i=c("convoy-issues-tbody");if(!i)return;k(i);const o=n.data.children??[];if(o.length===0){c("convoy-issues-empty").style.display="block";return}o.forEach(w=>{const C=w.assignee?w.assignee:w.status==="closed"?"done":"ready";i.append(s("tr",{},[s("td",{class:"convoy-issue-status"},[s("span",{class:`badge ${oe(w.status)}`},[w.status??"unknown"])]),s("td",{},[s("span",{class:"issue-id"},[w.id??""])]),s("td",{class:"issue-title"},[w.title??w.id??""]),s("td",{},[w.assignee?s("span",{class:"badge badge-blue"},[w.assignee]):s("span",{class:"badge badge-muted"},["Unassigned"])]),s("td",{},[C])]))}),c("convoy-issues-table").style.display="table"}function vs(){const e=c("convoy-detail"),t=(e==null?void 0:e.style.display)==="block";e.style.display="none",c("convoy-list").style.display="block",t&&P()}function dt(){const e=c("convoy-create-form"),t=(e==null?void 0:e.style.display)==="block";e.style.display="none",c("convoy-list").style.display="block",t&&P()}async function ws(){var r,i;const e=S();if(!e)return;const t=((r=c("convoy-create-name"))==null?void 0:r.value.trim())??"",n=(((i=c("convoy-create-issues"))==null?void 0:i.value)??"").split(/\s+/).map(o=>o.trim()).filter(Boolean);if(!t){v("error","Missing name","Convoy name is required");return}const a=await g.POST("/v0/city/{cityName}/convoys",{params:{path:{cityName:e}},body:{title:t,items:n}});if(a.error){v("error","Create failed",a.error.detail??"Could not create convoy");return}v("success","Convoy created",t),dt(),await wt()}async function Ss(){const e=S();if(!e||!ge)return;const t=c("convoy-add-issue-input"),n=(t==null?void 0:t.value.trim())??"";if(!n)return;const a=await g.POST("/v0/city/{cityName}/convoy/{id}/add",{params:{path:{cityName:e,id:ge}},body:{items:[n]}});if(a.error){v("error","Add failed",a.error.detail??"Could not add issue");return}t&&(t.value=""),c("convoy-add-issue-form").style.display="none",v("success","Issue added",n),await gn(ge),await wt()}function bn(e){var t,n;(n=(t=c("convoy-panel"))==null?void 0:t.scrollIntoView)==null||n.call(t,{behavior:"smooth",block:"center"}),window.setTimeout(()=>{var a;(a=c(e))==null||a.focus()},0)}const Cs=150,z=[];let X=null,je="all",Ie="all",Be="all";async function Es(e){z.splice(0,z.length,...vn(e)),Y()}async function ks(){var a;const e=S(),n=(((a=(e?await g.GET("/v0/city/{cityName}/events",{params:{path:{cityName:e},query:{since:"1h",limit:100}}}):await g.GET("/v0/events",{params:{query:{since:"1h"}}})).data)==null?void 0:a.items)??[]).map(r=>As(r)).filter(r=>r!==null);await Es(n)}function Ns(e,t){const n=S();X==null||X.close();const a=t?{onStatus:t}:void 0;X=(n?i=>Sa(n,i,a):i=>wa(i,a))(i=>{const o=Sn(i);e==null||e(i,o);const l=Ts(i);if(l){if(z.some(d=>d.id===l.id)){be("activity","Duplicate stream event ignored",{id:l.id,type:l.type});return}z.splice(0,z.length,...vn([l,...z])),Y()}})}function $s(){X==null||X.close(),X=null}function Y(){xs();const e=c("activity-feed");if(!e)return;k(e);const t=z.filter(a=>!(je!=="all"&&a.category!==je||Ie!=="all"&&a.rig!==Ie||Be!=="all"&&a.actor!==Be));if(c("activity-count").textContent=String(z.length),t.length===0){e.append(s("div",{class:"empty-state"},[s("p",{},["No recent activity"])]));return}const n=s("div",{class:"tl-timeline",id:"activity-timeline"});t.forEach(a=>{n.append(s("div",{class:`tl-entry ${_s(a.category)}`,"data-category":a.category,"data-rig":a.rig,"data-agent":a.actor??"","data-type":a.type,"data-ts":a.ts},[s("div",{class:"tl-rail"},[s("span",{class:"tl-time"},[mt(a.ts)]),s("span",{class:"tl-node"})]),s("div",{class:"tl-content"},[s("div",{class:"tl-header"},[s("span",{class:"tl-icon"},[sa(a.type)]),s("span",{class:"tl-summary"},[ra(a.type,a.actor,a.subject,a.message)])]),s("div",{class:"tl-meta"},[a.actor?s("span",{class:"tl-badge tl-badge-agent"},[I(a.actor)]):null,a.rig?s("span",{class:"tl-badge tl-badge-rig"},[a.rig]):null,s("span",{class:"tl-badge tl-badge-type"},[a.type])])])]))}),e.append(n)}function Ls(){var e,t;document.addEventListener("click",n=>{var r;const a=(r=n.target)==null?void 0:r.closest(".tl-filter-btn");a&&(je=a.dataset.value??"all",document.querySelectorAll(".tl-filter-btn").forEach(i=>i.classList.remove("active")),a.classList.add("active"),Y())}),(e=c("tl-rig-filter"))==null||e.addEventListener("change",n=>{Ie=n.currentTarget.value,Y()}),(t=c("tl-agent-filter"))==null||t.addEventListener("change",n=>{Be=n.currentTarget.value,Y()})}function xs(){const e=c("activity-filters");if(!e||(k(e),z.length===0))return;const t=[...new Set(z.map(i=>i.rig).filter(Boolean))].sort(),n=[...new Set(z.map(i=>i.actor).filter(Boolean))].sort(),a=s("select",{class:"tl-filter-select",id:"tl-rig-filter"});a.append(s("option",{value:"all"},["All rigs"])),t.forEach(i=>a.append(s("option",{value:i,selected:i===Ie},[i]))),a.addEventListener("change",()=>{Ie=a.value,Y()});const r=s("select",{class:"tl-filter-select",id:"tl-agent-filter"});r.append(s("option",{value:"all"},["All agents"])),n.forEach(i=>r.append(s("option",{value:i,selected:i===Be},[I(i)]))),r.addEventListener("change",()=>{Be=r.value,Y()}),e.append(s("div",{class:"tl-filters"},[s("div",{class:"tl-filter-group"},[s("label",{},["Category:"]),$e("all","All"),$e("agent","Agent"),$e("work","Work"),$e("comms","Comms"),$e("system","System")]),s("div",{class:"tl-filter-group"},[s("label",{},["Rig:"]),a]),s("div",{class:"tl-filter-group"},[s("label",{},["Agent:"]),r])]))}function $e(e,t){const n=s("button",{class:`tl-filter-btn${je===e?" active":""}`,"data-filter":"category","data-value":e,type:"button"},[t]);return n.addEventListener("click",()=>{je=e,Y()}),n}function Ts(e){return e.event==="heartbeat"?null:hn(e.data,e.id)}function As(e){return hn(e)}function hn(e,t){if(!e.type)return null;const n=wn(e)??S(),a=typeof e.seq=="number"?e.seq:0;return{id:qs(e,t),type:e.type,category:aa(e.type),actor:e.actor||void 0,subject:e.subject||void 0,message:e.message||void 0,ts:e.ts,scope:n,seq:a,rig:na(e.actor)||"city"in e&&e.city||""}}function vn(e){const t=new Map;return e.forEach(n=>{t.has(n.id)||t.set(n.id,n)}),[...t.values()].sort(Rs).slice(0,Cs)}function Rs(e,t){const n=Os(e.ts,t.ts);if(n!==0)return n;const a=e.scope.localeCompare(t.scope);if(a!==0)return a;const r=t.seq-e.seq;if(r!==0)return r;const i=e.type.localeCompare(t.type);if(i!==0)return i;const o=(e.actor??"").localeCompare(t.actor??"");return o!==0?o:(e.subject??"").localeCompare(t.subject??"")}function Os(e,t){const n=Number.isNaN(Date.parse(e))?0:Date.parse(e);return(Number.isNaN(Date.parse(t))?0:Date.parse(t))-n}function wn(e){if("city"in e&&typeof e.city=="string"&&e.city!=="")return e.city}function qs(e,t){const n=wn(e)??S();if(typeof e.seq=="number"&&e.seq>0)return`${n}:${e.seq}`;const a=[e.type,e.ts,e.actor??"",e.subject??"",e.message??"",t??""].join(":");return`${n}:${a}`}function Sn(e){return Ea(e)}function _s(e){switch(e){case"agent":return"activity-agent";case"work":return"activity-work";case"comms":return"activity-comms";default:return"activity-system"}}async function J(){var o,l,d,p,f,u;const e=S();if(!e){Ps();return}const[t,n,a,r,i]=await Promise.all([g.GET("/v0/city/{cityName}/services",{params:{path:{cityName:e}}}),g.GET("/v0/city/{cityName}/rigs",{params:{path:{cityName:e},query:{git:!0}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{label:"gc:escalation",status:"open",limit:200}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{status:"in_progress",limit:500}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{label:"gc:queue",limit:200}}})]);Is(((o=t.data)==null?void 0:o.items)??null,(l=t.error)==null?void 0:l.detail),Bs(((d=n.data)==null?void 0:d.items)??null),Ms(((p=a.data)==null?void 0:p.items)??null),Us(((f=r.data)==null?void 0:f.items)??null),Ds(((u=i.data)==null?void 0:u.items)??null)}function Ps(){Le("services-body","services-count","Select a city to view services"),Le("rigs-body","rigs-count","Select a city to view rigs"),Le("escalations-body","escalations-count","Select a city to view escalations"),Le("assigned-body","assigned-count","Select a city to view assigned work"),Le("queues-body","queues-count","Select a city to view queues"),c("clear-assigned-btn").style.display="none"}function js(){var e,t;(e=c("open-assign-btn"))==null||e.addEventListener("click",()=>{Cn()}),(t=c("clear-assigned-btn"))==null||t.addEventListener("click",()=>{Ws()})}function Is(e,t){const n=c("services-body"),a=c("services-count");if(!n||!a)return;if(k(n),t){a.textContent="n/a",n.append(s("div",{class:"empty-state"},[s("p",{},[t])]));return}const r=e??[];if(a.textContent=String(r.length),r.length===0){n.append(s("div",{class:"empty-state"},[s("p",{},["No workspace services"])]));return}const i=s("tbody");r.forEach(o=>{const l=s("button",{class:"esc-btn",type:"button"},["Restart"]);l.addEventListener("click",()=>{Hs(o.service_name)}),i.append(s("tr",{},[s("td",{},[s("strong",{},[o.service_name])]),s("td",{},[o.kind??"—"]),s("td",{},[s("span",{class:`badge ${oe(o.state??o.publication_state)}`},[o.state??o.publication_state??"unknown"])]),s("td",{},[o.local_state]),s("td",{},[l])]))}),n.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Name"]),s("th",{},["Kind"]),s("th",{},["Service"]),s("th",{},["Local"]),s("th",{},["Actions"])])]),i]))}function Bs(e){const t=c("rigs-body"),n=c("rigs-count");if(!t||!n)return;k(t);const a=e??[];if(n.textContent=String(a.length),a.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No rigs configured"])]));return}const r=s("tbody");a.forEach(i=>{var d;const o=s("button",{class:"esc-btn",type:"button"},[i.suspended?"Resume":"Suspend"]);o.addEventListener("click",()=>{_t(i.name,i.suspended?"resume":"suspend")});const l=s("button",{class:"esc-btn",type:"button"},["Restart"]);l.addEventListener("click",()=>{_t(i.name,"restart")}),r.append(s("tr",{},[s("td",{},[s("span",{class:"rig-name"},[i.name])]),s("td",{},[String(i.agent_count-i.running_count)]),s("td",{},[String(i.running_count)]),s("td",{},[(d=i.git)!=null&&d.branch?`${i.git.branch}${i.git.clean?"":"*"}`:"—"]),s("td",{},[U(i.last_activity)]),s("td",{},[o," ",l])]))}),t.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Name"]),s("th",{},["Idle"]),s("th",{},["Running"]),s("th",{},["Git"]),s("th",{},["Activity"]),s("th",{},["Actions"])])]),r]))}function Ms(e){const t=c("escalations-body"),n=c("escalations-count");if(!t||!n)return;k(t);const a=(e??[]).sort((i,o)=>(i.created_at??"").localeCompare(o.created_at??""));if(n.textContent=String(a.length),a.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No escalations"])]));return}const r=s("tbody");a.forEach(i=>{const o=zs(i.labels??[]),l=(i.labels??[]).includes("acked"),d=s("button",{class:"esc-btn esc-ack-btn",type:"button"},["👍 Ack"]);d.addEventListener("click",()=>{Js(i)});const p=s("button",{class:"esc-btn esc-resolve-btn",type:"button"},["✓ Resolve"]);p.addEventListener("click",()=>{i.id&&Vs(i.id)});const f=s("button",{class:"esc-btn esc-reassign-btn",type:"button"},["↻ Reassign"]);f.addEventListener("click",()=>{i.id&&Ks(i.id)}),r.append(s("tr",{class:"escalation-row","data-escalation-id":i.id??""},[s("td",{},[s("span",{class:`badge ${Gs(o)}`},[o.toUpperCase()])]),s("td",{},[i.title??i.id??"",l?s("span",{class:"badge badge-cyan",style:"margin-left: 4px;"},["ACK"]):null]),s("td",{},[I(i.assignee)]),s("td",{},[U(i.created_at)]),s("td",{class:"escalation-actions"},[l?null:d,p,f])]))}),t.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Severity"]),s("th",{},["Issue"]),s("th",{},["From"]),s("th",{},["Age"]),s("th",{},["Actions"])])]),r]))}function Us(e){const t=c("assigned-body"),n=c("assigned-count"),a=c("clear-assigned-btn");if(!t||!n||!a)return;k(t);const r=(e??[]).filter(o=>o.assignee);if(n.textContent=String(r.length),a.style.display=r.length>0?"inline-flex":"none",r.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No assigned work"])]));return}const i=s("tbody");r.forEach(o=>{const l=s("button",{class:"unassign-btn",type:"button"},["Unassign"]);l.addEventListener("click",()=>{o.id&&Fs(o.id)}),i.append(s("tr",{},[s("td",{},[s("span",{class:"assigned-id"},[o.id??""])]),s("td",{class:"assigned-title"},[Ke(o.title??"",80)]),s("td",{class:"assigned-agent"},[I(o.assignee)]),s("td",{class:"assigned-age"},[U(o.created_at)]),s("td",{},[l])]))}),t.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Bead"]),s("th",{},["Title"]),s("th",{},["Agent"]),s("th",{},["Since"]),s("th",{},[""])])]),i]))}function Ds(e){const t=c("queues-body"),n=c("queues-count");if(!t||!n)return;k(t);const a=e??[];if(n.textContent=String(a.length),a.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No queues"])]));return}const r=s("tbody");a.forEach(i=>{r.append(s("tr",{},[s("td",{},[i.title??i.id??"queue"]),s("td",{},[i.id??"—"]),s("td",{},[s("span",{class:`badge ${oe(i.status)}`},[i.status??"open"])]),s("td",{},[I(i.assignee)]),s("td",{},[U(i.created_at)])]))}),t.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Queue"]),s("th",{},["Bead"]),s("th",{},["Status"]),s("th",{},["Assignee"]),s("th",{},["Created"])])]),r]))}function Le(e,t,n){const a=c(e),r=c(t);!a||!r||(k(a),r.textContent="0",a.append(s("div",{class:"empty-state"},[s("p",{},[n])])))}function zs(e){for(const t of e)if(t.startsWith("severity:"))return t.slice(9);return"medium"}function Gs(e){switch(e){case"critical":return"badge-red";case"high":return"badge-orange";case"low":return"badge-muted";default:return"badge-yellow"}}async function Cn(e=""){const t=S();if(!t)return;const n=await bt({beadID:e||void 0,beadLabel:e||void 0,mode:"assign",title:"Assign Work"});if(!n)return;const a=await g.POST("/v0/city/{cityName}/sling",{params:{path:{cityName:t}},body:{bead:n.beadID,target:n.target,rig:n.rig||void 0}});if(a.error){v("error","Assign failed",a.error.detail??"Could not assign bead");return}v("success","Assigned",`${n.beadID} → ${n.target}`),await J()}async function Ws(){var r;const e=S();if(!e)return;const n=(((r=(await g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{status:"in_progress",limit:500}}})).data)==null?void 0:r.items)??[]).filter(i=>i.assignee);if(n.length===0){v("info","Nothing to clear","No assigned work");return}await Ba({body:`Unassign ${n.length} active ${n.length===1?"bead":"beads"}?`,confirmLabel:"Unassign All",title:"Clear Assignments"})&&(await Promise.all(n.map(i=>g.POST("/v0/city/{cityName}/bead/{id}/assign",{params:{path:{cityName:e,id:i.id??""}},body:{assignee:""}}))),v("success","Cleared",`${n.length} assignments removed`),await J())}async function Fs(e){const t=S();if(!t)return;const n=await g.POST("/v0/city/{cityName}/bead/{id}/assign",{params:{path:{cityName:t,id:e}},body:{assignee:""}});if(n.error){v("error","Unassign failed",n.error.detail??"Could not unassign bead");return}v("success","Unassigned",e),await J()}async function Hs(e){const t=S();if(!t)return;const n=await g.POST("/v0/city/{cityName}/service/{name}/restart",{params:{path:{cityName:t,name:e}}});if(n.error){v("error","Service failed",n.error.detail??"Could not restart service");return}v("success","Service restarted",e),await J()}async function _t(e,t){const n=S();if(!n)return;const a=await g.POST("/v0/city/{cityName}/rig/{name}/{action}",{params:{path:{cityName:n,name:e,action:t}}});if(a.error){v("error","Rig action failed",a.error.detail??`Could not ${t} ${e}`);return}v("success","Rig updated",`${e}: ${t}`),await J()}async function Js(e){const t=S();if(!t||!e.id)return;const n=Array.from(new Set([...e.labels??[],"acked"])),a=await g.POST("/v0/city/{cityName}/bead/{id}/update",{params:{path:{cityName:t,id:e.id}},body:{labels:n}});if(a.error){v("error","Ack failed",a.error.detail??"Could not acknowledge escalation");return}v("success","Acknowledged",e.id),await J()}async function Vs(e){const t=S();if(!t)return;const n=await g.POST("/v0/city/{cityName}/bead/{id}/close",{params:{path:{cityName:t,id:e}}});if(n.error){v("error","Resolve failed",n.error.detail??"Could not resolve escalation");return}v("success","Resolved",e),await J()}async function Ks(e){const t=S();if(!t)return;const n=await bt({beadID:e,beadLabel:e,mode:"reassign",title:"Reassign Escalation"});if(!n)return;const a=await g.POST("/v0/city/{cityName}/bead/{id}/assign",{params:{path:{cityName:t,id:e}},body:{assignee:n.target}});if(a.error){v("error","Reassign failed",a.error.detail??"Could not reassign escalation");return}v("success","Reassigned",`${e} → ${n.target||"unassigned"}`),await J()}function Qs(e){const t=c("command-palette-overlay"),n=c("command-palette-input"),a=c("command-palette-results"),r=c("open-palette-btn");if(!t||!n||!a||!r)return;const i=t,o=n,l=a,d=r;let p=[],f=[],u=0;function y(){const b=S(),E=async(N,j)=>{const B=await j;At(N,JSON.stringify(B,null,2))};return[{name:"refresh",desc:"Refresh all panels",category:"Dashboard",run:()=>e.refreshAll()},{name:"supervisor health",desc:"Show supervisor health JSON",category:"Supervisor",run:()=>E("health",g.GET("/health"))},{name:"city list",desc:"Show managed cities JSON",category:"Supervisor",run:()=>E("cities",g.GET("/v0/cities"))},{name:"global events",desc:"Show recent supervisor events JSON",category:"Supervisor",run:()=>E("events",g.GET("/v0/events",{params:{query:{since:"1h"}}}))},...b?[{name:"new issue",desc:"Open the issue creation modal",category:"Work",run:()=>ln()},{name:"compose mail",desc:"Open the compose mail form",category:"Mail",run:()=>lt()},{name:"new convoy",desc:"Open the convoy creation form",category:"Convoys",run:()=>mn()},{name:"assign work",desc:"Open the assignment modal",category:"Assigned",run:()=>Cn()},{name:"status",desc:"Show current city status JSON",category:"Status",run:()=>E("status",g.GET("/v0/city/{cityName}/status",{params:{path:{cityName:b}}}))},{name:"agent list",desc:"Show current sessions JSON",category:"Status",run:()=>E("sessions",g.GET("/v0/city/{cityName}/sessions",{params:{path:{cityName:b},query:{state:"active",peek:!0}}}))},{name:"convoy list",desc:"Show current convoys JSON",category:"Convoys",run:()=>E("convoys",g.GET("/v0/city/{cityName}/convoys",{params:{path:{cityName:b},query:{limit:200}}}))},{name:"mail inbox",desc:"Show current mail JSON",category:"Mail",run:()=>E("mail",g.GET("/v0/city/{cityName}/mail",{params:{path:{cityName:b},query:{status:"all",limit:200}}}))},{name:"rig list",desc:"Show rig JSON",category:"Rigs",run:()=>E("rigs",g.GET("/v0/city/{cityName}/rigs",{params:{path:{cityName:b},query:{git:!0}}}))},{name:"list",desc:"Show open and in-progress beads JSON",category:"Beads",run:async()=>{var B,$;const[N,j]=await Promise.all([g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:b},query:{status:"open",limit:500}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:b},query:{status:"in_progress",limit:500}}})]);At("beads",JSON.stringify({open:((B=N.data)==null?void 0:B.items)??[],in_progress:(($=j.data)==null?void 0:$.items)??[]},null,2))}}]:[],{name:"close output",desc:"Hide the output panel",category:"Dashboard",run:()=>Yt()}].filter(N=>typeof N.run=="function")}function m(){k(l);const b=o.value.trim().toLowerCase();if(p=y(),f=p.filter(E=>b===""||E.name.includes(b)||E.desc.toLowerCase().includes(b)||E.category.toLowerCase().includes(b)),u>=f.length&&(u=0),f.length===0){l.append(s("div",{class:"command-palette-empty"},["No matching commands"]));return}f.forEach((E,N)=>{const j=s("button",{class:`command-item${N===u?" selected":""}`,type:"button"},[s("span",{class:"command-name"},[`gt ${E.name}`]),s("span",{class:"command-desc"},[E.desc]),s("span",{class:"command-category"},[E.category])]);j.addEventListener("click",()=>{C(N)}),l.append(j)})}function h(){i.classList.add("open"),o.value="",u=0,m(),o.focus()}function w(){i.classList.remove("open")}async function C(b){const E=f[b];w(),E&&(H("palette","Execute command",{category:E.category,city:S(),command:E.name}),await E.run())}d.addEventListener("click",()=>h()),i.addEventListener("click",b=>{b.target===i&&w()}),o.addEventListener("input",()=>m()),o.addEventListener("keydown",b=>{if(b.key==="ArrowDown"){u=Math.min(u+1,Math.max(f.length-1,0)),m(),b.preventDefault();return}if(b.key==="ArrowUp"){u=Math.max(u-1,0),m(),b.preventDefault();return}if(b.key==="Enter"){C(u),b.preventDefault();return}b.key==="Escape"&&w()}),document.addEventListener("keydown",b=>{(b.metaKey||b.ctrlKey)&&b.key.toLowerCase()==="k"&&(b.preventDefault(),i.classList.contains("open")?w():h())})}function Xs(){const e=c("supervisor-overview-panel"),t=c("supervisor-overview-body"),n=c("supervisor-city-count");if(!e||!t||!n)return;const a=S()==="";if(e.hidden=!a,!a)return;const r=Ft().sort((o,l)=>o.name.localeCompare(l.name));if(n.textContent=String(r.length),k(t),r.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No managed cities available"])]));return}const i=s("tbody");r.forEach(o=>{const l=o.phasesCompleted.length>0?o.phasesCompleted.join(", "):"—",d=s("a",{class:"supervisor-city-link",href:`?city=${encodeURIComponent(o.name)}`},["Open"]);i.append(s("tr",{},[s("td",{},[s("strong",{},[o.name])]),s("td",{},[s("span",{class:`badge ${o.error?"badge-red":o.running?"badge-green":"badge-muted"}`},[o.error?"Error":o.running?"Running":"Stopped"])]),s("td",{},[o.status??"—"]),s("td",{class:"supervisor-city-phases"},[l]),s("td",{class:"supervisor-city-error"},[o.error??"—"]),s("td",{class:"supervisor-city-actions"},[d])]))}),t.append(s("table",{class:"supervisor-city-table"},[s("thead",{},[s("tr",{},[s("th",{},["City"]),s("th",{},["State"]),s("th",{},["Status"]),s("th",{},["Phases"]),s("th",{},["Error"]),s("th",{},[""])])]),i]))}const Ys=["convoy-panel","crew-panel","rigged-panel","mail-panel","escalations-panel","services-panel","rigs-panel","pooled-panel","queues-panel","beads-panel","assigned-panel","agent-log-drawer"];async function Zs(){gt()||await Se()}async function er(){gt()||await Se().catch(e=>_("Catch-up refresh failed",e))}async function tr(){yt(),await Se(!0)}function St(){const e=Ht();if(e.kind==="not-running"||e.kind==="unknown"){$s(),at("connecting");return}at("connecting"),Ns(t=>{const n=Sn(t);!n||n==="heartbeat"||(Zn(n),!gt()&&Se().catch(a=>_("Refresh failed",a)))},at)}function at(e){const t=Ct("connection-status");if(!t)return;const n={connecting:"Connecting…",live:"Live",reconnecting:"Reconnecting…"};t.replaceChildren(document.createTextNode(n[e])),t.classList.remove("connection-live","connection-connecting","connection-reconnecting"),t.classList.add(`connection-${e}`)}function nr(){ga(),Ia(),Aa(),Ga(),cs(),hs(),Ls(),js(),Qs({refreshAll:Zs})}async function ar(){Jn(),H("dashboard","Boot start",{city:S(),href:window.location.href}),nr(),rr(),ma(()=>{er()}),await tr(),St(),H("dashboard","Boot complete",{city:S(),href:window.location.href})}function Ct(e){return document.getElementById(e)}ar().catch(e=>_("Dashboard boot failed",e));function sr(){const e=S()!=="";or(e),Ue("new-convoy-btn",e,"Select a city to create a convoy"),Ue("new-issue-btn",e,"Select a city to create a bead"),Ue("compose-mail-btn",e,"Select a city to compose mail"),Ue("open-assign-btn",e,"Select a city to assign work")}function Ue(e,t,n){const a=Ct(e);a&&(a.dataset.defaultTitle===void 0&&(a.dataset.defaultTitle=a.title||""),a.disabled=!t,a.title=t?a.dataset.defaultTitle:n)}function rr(){document.addEventListener("click",e=>{var a;const t=(a=e.target)==null?void 0:a.closest("a.city-tab");if(!t)return;const n=t.href;!n||n===window.location.href||(e.preventDefault(),ir(n))}),window.addEventListener("popstate",()=>{H("dashboard","Popstate navigation",{href:window.location.href}),rn(),pt(),yt(),Se().catch(e=>_("Refresh failed",e)),St()})}async function ir(e){H("dashboard","Navigate city scope",{nextURL:e}),rn(),window.history.pushState({},"",e),pt(),yt(),await Se(),St()}function or(e){Ys.forEach(t=>{const n=Ct(t);if(!n)return;const a=!e&&n.classList.contains("expanded");if(n.hidden=!e,a){n.classList.remove("expanded");const r=n.querySelector(".expand-btn");r&&(r.textContent="Expand"),P()}})}async function Se(e=!1){pt(),sr();const t=Xn(e);if(t.size===0)return;t.has("options")&&ja(),t.has("cities")&&await ea().catch(l=>_("City tabs failed",l));const n=[],r=Ht().kind==="running";ne(n,t,"status",()=>ia()),ne(n,t,"activity",()=>ks()),r&&(ne(n,t,"crew",()=>ka()),ne(n,t,"issues",()=>le()),ne(n,t,"mail",()=>Me()),ne(n,t,"convoys",()=>wt()),ne(n,t,"admin",()=>J()));const o=(await Promise.allSettled(n)).find(l=>l.status==="rejected");o&&_("Panel refresh failed",o.reason),(t.has("supervisor")||t.has("cities"))&&Xs()}function ne(e,t,n,a){t.has(n)&&e.push(a())} +`);A=te.pop()??"";for(const pe of te){const G=pe.split(` +`),x=[];let ye;for(const _ of G)if(_.startsWith("data:"))x.push(_.replace(/^data:\s*/,""));else if(_.startsWith("event:"))ye=_.replace(/^event:\s*/,"");else if(_.startsWith("id:"))u=_.replace(/^id:\s*/,"");else if(_.startsWith("retry:")){const ne=Number.parseInt(_.replace(/^retry:\s*/,""),10);Number.isNaN(ne)||(w=ne)}let R,q=!1;if(x.length){const _=x.join(` +`);try{R=JSON.parse(_),q=!0}catch{R=_}}q&&(r&&await r(R),a&&(R=await a(R))),n==null||n({data:R,event:ye,id:u,retry:w}),x.length&&(yield R)}}}finally{b.removeEventListener("abort",fe),O.releaseLock()}break}catch(N){if(t==null||t(N),o!==void 0&&E>=o)break;const I=Math.min(w*2**(E-1),l??3e4);await y(I)}}}()}}const qn=e=>{switch(e){case"label":return".";case"matrix":return";";case"simple":return",";default:return"&"}},_n=e=>{switch(e){case"form":return",";case"pipeDelimited":return"|";case"spaceDelimited":return"%20";default:return","}},Pn=e=>{switch(e){case"label":return".";case"matrix":return";";case"simple":return",";default:return"&"}},Bt=({allowReserved:e,explode:t,name:n,style:a,value:r})=>{if(!t){const l=(e?r:r.map(d=>encodeURIComponent(d))).join(_n(a));switch(a){case"label":return`.${l}`;case"matrix":return`;${n}=${l}`;case"simple":return l;default:return`${n}=${l}`}}const i=qn(a),o=r.map(l=>a==="label"||a==="simple"?e?l:encodeURIComponent(l):Je({allowReserved:e,name:n,value:l})).join(i);return a==="label"||a==="matrix"?i+o:o},Je=({allowReserved:e,name:t,value:n})=>{if(n==null)return"";if(typeof n=="object")throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");return`${t}=${e?n:encodeURIComponent(n)}`},Mt=({allowReserved:e,explode:t,name:n,style:a,value:r,valueOnly:i})=>{if(r instanceof Date)return i?r.toISOString():`${n}=${r.toISOString()}`;if(a!=="deepObject"&&!t){let d=[];Object.entries(r).forEach(([f,u])=>{d=[...d,f,e?u:encodeURIComponent(u)]});const p=d.join(",");switch(a){case"form":return`${n}=${p}`;case"label":return`.${p}`;case"matrix":return`;${n}=${p}`;default:return p}}const o=Pn(a),l=Object.entries(r).map(([d,p])=>Je({allowReserved:e,name:a==="deepObject"?`${n}[${d}]`:d,value:p})).join(o);return a==="label"||a==="matrix"?o+l:l},jn=/\{[^{}]+\}/g,In=({path:e,url:t})=>{let n=t;const a=t.match(jn);if(a)for(const r of a){let i=!1,o=r.substring(1,r.length-1),l="simple";o.endsWith("*")&&(i=!0,o=o.substring(0,o.length-1)),o.startsWith(".")?(o=o.substring(1),l="label"):o.startsWith(";")&&(o=o.substring(1),l="matrix");const d=e[o];if(d==null)continue;if(Array.isArray(d)){n=n.replace(r,Bt({explode:i,name:o,style:l,value:d}));continue}if(typeof d=="object"){n=n.replace(r,Mt({explode:i,name:o,style:l,value:d,valueOnly:!0}));continue}if(l==="matrix"){n=n.replace(r,`;${Je({name:o,value:d})}`);continue}const p=encodeURIComponent(l==="label"?`.${d}`:d);n=n.replace(r,p)}return n},Bn=({baseUrl:e,path:t,query:n,querySerializer:a,url:r})=>{const i=r.startsWith("/")?r:`/${r}`;let o=(e??"")+i;t&&(o=In({path:t,url:o}));let l=n?a(n):"";return l.startsWith("?")&&(l=l.substring(1)),l&&(o+=`?${l}`),o};function Lt(e){const t=e.body!==void 0;if(t&&e.bodySerializer)return"serializedBody"in e?e.serializedBody!==void 0&&e.serializedBody!==""?e.serializedBody:null:e.body!==""?e.body:null;if(t)return e.body}const Mn=async(e,t)=>{const n=typeof t=="function"?await t(e):t;if(n)return e.scheme==="bearer"?`Bearer ${n}`:e.scheme==="basic"?`Basic ${btoa(n)}`:n},Ut=({parameters:e={},...t}={})=>a=>{const r=[];if(a&&typeof a=="object")for(const i in a){const o=a[i];if(o==null)continue;const l=e[i]||t;if(Array.isArray(o)){const d=Bt({allowReserved:l.allowReserved,explode:!0,name:i,style:"form",value:o,...l.array});d&&r.push(d)}else if(typeof o=="object"){const d=Mt({allowReserved:l.allowReserved,explode:!0,name:i,style:"deepObject",value:o,...l.object});d&&r.push(d)}else{const d=Je({allowReserved:l.allowReserved,name:i,value:o});d&&r.push(d)}}return r.join("&")},Un=e=>{var n;if(!e)return"stream";const t=(n=e.split(";")[0])==null?void 0:n.trim();if(t){if(t.startsWith("application/json")||t.endsWith("+json"))return"json";if(t==="multipart/form-data")return"formData";if(["application/","audio/","image/","video/"].some(a=>t.startsWith(a)))return"blob";if(t.startsWith("text/"))return"text"}},Dn=(e,t)=>{var n,a;return t?!!(e.headers.has(t)||(n=e.query)!=null&&n[t]||(a=e.headers.get("Cookie"))!=null&&a.includes(`${t}=`)):!1},zn=async({security:e,...t})=>{for(const n of e){if(Dn(t,n.name))continue;const a=await Mn(n,t.auth);if(!a)continue;const r=n.name??"Authorization";switch(n.in){case"query":t.query||(t.query={}),t.query[r]=a;break;case"cookie":t.headers.append("Cookie",`${r}=${a}`);break;case"header":default:t.headers.set(r,a);break}}},xt=e=>Bn({baseUrl:e.baseUrl,path:e.path,query:e.query,querySerializer:typeof e.querySerializer=="function"?e.querySerializer:Ut(e.querySerializer),url:e.url}),Tt=(e,t)=>{var a;const n={...e,...t};return(a=n.baseUrl)!=null&&a.endsWith("/")&&(n.baseUrl=n.baseUrl.substring(0,n.baseUrl.length-1)),n.headers=Dt(e.headers,t.headers),n},Wn=e=>{const t=[];return e.forEach((n,a)=>{t.push([a,n])}),t},Dt=(...e)=>{const t=new Headers;for(const n of e){if(!n)continue;const a=n instanceof Headers?Wn(n):Object.entries(n);for(const[r,i]of a)if(i===null)t.delete(r);else if(Array.isArray(i))for(const o of i)t.append(r,o);else i!==void 0&&t.set(r,typeof i=="object"?JSON.stringify(i):i)}return t};class tt{constructor(){this.fns=[]}clear(){this.fns=[]}eject(t){const n=this.getInterceptorIndex(t);this.fns[n]&&(this.fns[n]=null)}exists(t){const n=this.getInterceptorIndex(t);return!!this.fns[n]}getInterceptorIndex(t){return typeof t=="number"?this.fns[t]?t:-1:this.fns.indexOf(t)}update(t,n){const a=this.getInterceptorIndex(t);return this.fns[a]?(this.fns[a]=n,t):!1}use(t){return this.fns.push(t),this.fns.length-1}}const Gn=()=>({error:new tt,request:new tt,response:new tt}),Fn=Ut({allowReserved:!1,array:{explode:!0,style:"form"},object:{explode:!0,style:"deepObject"}}),Hn={"Content-Type":"application/json"},zt=(e={})=>({...Rn,headers:Hn,parseAs:"auto",querySerializer:Fn,...e}),Jn=(e={})=>{let t=Tt(zt(),e);const n=()=>({...t}),a=f=>(t=Tt(t,f),n()),r=Gn(),i=async f=>{const u={...t,...f,fetch:f.fetch??t.fetch??globalThis.fetch,headers:Dt(t.headers,f.headers),serializedBody:void 0};u.security&&await zn({...u,security:u.security}),u.requestValidator&&await u.requestValidator(u),u.body!==void 0&&u.bodySerializer&&(u.serializedBody=u.bodySerializer(u.body)),(u.body===void 0||u.serializedBody==="")&&u.headers.delete("Content-Type");const y=u,m=xt(y);return{opts:y,url:m}},o=async f=>{const{opts:u,url:y}=await i(f),m={redirect:"follow",...u,body:Lt(u)};let h=new Request(y,m);for(const $ of r.request.fns)$&&(h=await $(h,u));const w=u.fetch;let E;try{E=await w(h)}catch($){let O=$;for(const A of r.error.fns)A&&(O=await A($,void 0,h,u));if(O=O||{},u.throwOnError)throw O;return u.responseStyle==="data"?void 0:{error:O,request:h,response:void 0}}for(const $ of r.response.fns)$&&(E=await $(E,h,u));const b={request:h,response:E};if(E.ok){const $=(u.parseAs==="auto"?Un(E.headers.get("Content-Type")):u.parseAs)??"json";if(E.status===204||E.headers.get("Content-Length")==="0"){let A;switch($){case"arrayBuffer":case"blob":case"text":A=await E[$]();break;case"formData":A=new FormData;break;case"stream":A=E.body;break;case"json":default:A={};break}return u.responseStyle==="data"?A:{data:A,...b}}let O;switch($){case"arrayBuffer":case"blob":case"formData":case"text":O=await E[$]();break;case"json":{const A=await E.text();O=A?JSON.parse(A):{};break}case"stream":return u.responseStyle==="data"?E.body:{data:E.body,...b}}return $==="json"&&(u.responseValidator&&await u.responseValidator(O),u.responseTransformer&&(O=await u.responseTransformer(O))),u.responseStyle==="data"?O:{data:O,...b}}const C=await E.text();let N;try{N=JSON.parse(C)}catch{}const I=N??C;let M=I;for(const $ of r.error.fns)$&&(M=await $(I,E,h,u));if(M=M||{},u.throwOnError)throw M;return u.responseStyle==="data"?void 0:{error:M,...b}},l=f=>u=>o({...u,method:f}),d=f=>async u=>{const{opts:y,url:m}=await i(u);return On({...y,body:y.body,headers:y.headers,method:f,onRequest:async(h,w)=>{let E=new Request(h,w);for(const b of r.request.fns)b&&(E=await b(E,y));return E},serializedBody:Lt(y),url:m})};return{buildUrl:f=>xt({...t,...f}),connect:l("CONNECT"),delete:l("DELETE"),get:l("GET"),getConfig:n,head:l("HEAD"),interceptors:r,options:l("OPTIONS"),patch:l("PATCH"),post:l("POST"),put:l("PUT"),request:o,setConfig:a,sse:{connect:d("CONNECT"),delete:d("DELETE"),get:d("GET"),head:d("HEAD"),options:d("OPTIONS"),patch:d("PATCH"),post:d("POST"),put:d("PUT"),trace:d("TRACE")},trace:l("TRACE")}},le=Jn(zt()),Wt={debug:console.debug.bind(console),error:console.error.bind(console),info:console.info.bind(console),log:console.log.bind(console),warn:console.warn.bind(console)};let At=!1;function Vn(){At||typeof window>"u"||(At=!0,ke("debug","debug"),ke("info","info"),ke("warn","warn"),ke("error","error"),ke("log","info"),window.addEventListener("error",e=>{oe("window","Unhandled error",{colno:e.colno,error:e.error,filename:e.filename,lineno:e.lineno,message:e.message})}),window.addEventListener("unhandledrejection",e=>{oe("window","Unhandled promise rejection",{reason:e.reason})}))}function he(e,t,n){Ke("debug",e,t,n)}function J(e,t,n){Ke("info",e,t,n)}function Ve(e,t,n){Ke("warn",e,t,n)}function oe(e,t,n){Ke("error",e,t,n)}function Ke(e,t,n,a){const r=Gt(e,t,n,a);Wt[e](`[dashboard][${t}] ${n}`,We(a)),Ft(r)}function ke(e,t){const n=Wt[e];console[e]=(...a)=>{n(...a),Ft(Gt(t,"console",Qn(a),a.length>1?a.slice(1):a[0]))}}function Gt(e,t,n,a){return{city:Kn(),details:a===void 0?void 0:We(a),level:e,message:n,scope:t,ts:new Date().toISOString(),url:typeof window>"u"?"":window.location.href}}function Kn(){return typeof window>"u"?"":(new URLSearchParams(window.location.search).get("city")??"").trim()}function Qn(e){if(e.length===0)return"console event";const[t]=e;return typeof t=="string"&&t.trim()!==""?t:t instanceof Error?t.message:"console event"}function Ft(e){const t=JSON.stringify(e);if(typeof navigator<"u"&&typeof navigator.sendBeacon=="function"){const n=new Blob([t],{type:"application/json"});if(navigator.sendBeacon("/__client-log",n))return}fetch("/__client-log",{body:t,credentials:"same-origin",headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})}function We(e,t=0,n=new WeakSet){if(e==null)return e??null;if(typeof e=="string")return e.length>2e3?`${e.slice(0,1999)}…`:e;if(typeof e=="number"||typeof e=="boolean")return e;if(e instanceof Error)return{message:e.message,name:e.name,stack:e.stack};if(typeof e=="function")return`[function ${e.name||"anonymous"}]`;if(t>=4)return"[max-depth]";if(Array.isArray(e))return e.slice(0,20).map(a=>We(a,t+1,n));if(typeof e=="object"){if(n.has(e))return"[circular]";n.add(e);const a={};for(const[r,i]of Object.entries(e).slice(0,40))a[r]=We(i,t+1,n);return a}return String(e)}const ft=["cities","status","supervisor","crew","issues","mail","convoys","activity","admin","options"];let Ge=Vt(window.location.search),pt=[];const ze=new Set(ft);function Xn(){return Ge}function yt(){return Ge=Vt(window.location.search),Ge}function se(...e){e.forEach(t=>ze.add(t))}function mt(){se(...ft)}function Yn(e=!1){if(e)return ze.clear(),new Set(ft);const t=new Set(ze);return ze.clear(),t}function Zn(e){pt=e.map(t=>({error:t.error,name:t.name,path:t.path,phasesCompleted:[...t.phasesCompleted??[]],running:t.running,status:t.status}))}function Ht(){return pt.map(e=>({error:e.error,name:e.name,path:e.path,phasesCompleted:[...e.phasesCompleted],running:e.running,status:e.status}))}function Jt(){const e=Ge;if(e==="")return{kind:"supervisor"};const t=pt.find(n=>n.name===e);return t?t.running?{kind:"running",city:t}:{kind:"not-running",city:t}:{kind:"unknown",name:e}}function ea(e){if(e){if(e.startsWith("session.")||e.startsWith("agent.")){se("status","crew","options");return}if(e.startsWith("bead.")){se("status","issues","convoys","admin","options");return}if(e.startsWith("mail.")){se("status","mail","options");return}if(e.startsWith("convoy.")){se("status","convoys");return}if(e.startsWith("city.")){se("cities","status","supervisor");return}if(e.startsWith("service.")||e.startsWith("provider.")||e.startsWith("rig.")){se("admin");return}}}function Vt(e){return(new URLSearchParams(e).get("city")??"").trim()}function Kt(){const e=document.querySelector('meta[name="supervisor-url"]');return((e==null?void 0:e.content)??"").replace(/\/+$/,"")}function S(){return Xn()}const T={"X-GC-Request":"true"},g=Ln({baseUrl:Kt(),headers:T});le.setConfig({baseUrl:Kt(),headers:T});g.use({async onError({error:e,request:t,schemaPath:n}){return oe("api","Request failed",{error:e,method:t.method,schemaPath:n,url:t.url}),e instanceof Error?e:new Error(String(e))},async onRequest({params:e,request:t,schemaPath:n}){he("api","Request start",{method:t.method,params:e,schemaPath:n,url:t.url})},async onResponse({request:e,response:t,schemaPath:n}){const a={method:e.method,ok:t.ok,schemaPath:n,status:t.status,url:e.url};if(!t.ok||t.status>=400){Ve("api","Request response",a);return}he("api","Request response",a)}});function s(e,t={},n=[]){const a=document.createElement(e);for(const[r,i]of Object.entries(t))i===void 0||i===!1||(i===!0?a.setAttribute(r,""):a.setAttribute(r,String(i)));for(const r of n)r!=null&&a.append(typeof r=="string"?document.createTextNode(r):r);return a}function k(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function c(e){return document.getElementById(e)}async function ta(){const e=c("city-tabs");if(!e)return;const{data:t,error:n}=await g.GET("/v0/cities");!n&&(t!=null&&t.items)&&Zn(t.items.map(l=>({error:l.error??void 0,name:l.name??"",path:l.path??void 0,phasesCompleted:l.phases_completed??[],running:l.running===!0,status:l.status??void 0})));const a=Ht();if(n||a.length===0)return;const r=S();k(e);const i=s("nav",{class:"city-tabs"}),o=window.location.pathname||"/";i.append(s("a",{href:o,class:`city-tab${r===""?" active":""}`},[s("span",{class:"city-dot running"})," Supervisor"]));for(const l of a){const d=l.running,p=l.name===r,f=s("a",{href:`${o}?city=${encodeURIComponent(l.name)}`,class:`city-tab${p?" active":""}${d?"":" stopped"}`},[s("span",{class:`city-dot${d?" running":""}`}),` ${l.name}`]);i.append(f)}e.append(i)}function gt(e,t=new Date){if(!e)return"";const n=new Date(e);if(isNaN(n.getTime()))return"";const a=Math.max(0,t.getTime()-n.getTime()),r=Math.floor(a/1e3);if(r<60)return`${r}s ago`;const i=Math.floor(r/60);if(i<60)return`${i}m ago`;const o=Math.floor(i/60);return o<24?`${o}h ago`:`${Math.floor(o/24)}d ago`}const Qt=300*1e3,na=600*1e3;function D(e){if(!e)return"—";const t=new Date(e);if(Number.isNaN(t.getTime()))return"—";const n=new Date,a=t.getFullYear()===n.getFullYear()?{month:"short",day:"numeric",hour:"numeric",minute:"2-digit"}:{month:"short",day:"numeric",year:"numeric",hour:"numeric",minute:"2-digit"};return t.toLocaleString(void 0,a)}function _e(e){if(!e)return{display:"unknown",colorClass:"unknown"};const t=new Date(e);if(Number.isNaN(t.getTime()))return{display:"unknown",colorClass:"unknown"};const n=Math.max(0,Date.now()-t.getTime()),a=gt(e).replace(" ago","");return n=3?`${t[t.length-1]} (${t[0]}/${t[1]})`:`${t[0]}/${t[t.length-1]}`}function aa(e){return!e||!e.includes("/")?"":e.split("/",1)[0]??""}function sa(e){return e.startsWith("agent.")||e.startsWith("session.")?"agent":e.startsWith("bead.")||e.startsWith("convoy.")||e.startsWith("order.")?"work":e.startsWith("mail.")?"comms":"system"}function ra(e){return{"session.started":"▶","session.ended":"■","session.crashed":"☠","session.suspended":"⏸","session.woke":"▶","agent.message":"💬","agent.output":"📝","agent.tool_call":"🛠","agent.tool_result":"✅","agent.error":"⚠","bead.created":"📿","bead.updated":"📝","bead.closed":"✅","convoy.created":"🚚","convoy.closed":"✅","mail.delivered":"📬","mail.read":"📨"}[e]??"📋"}function ia(e,t,n,a){const r=B(t);switch(e){case"session.started":return`${B(n)} started`;case"session.ended":return`${B(n)} ended`;case"session.crashed":return`${B(n)} crashed`;case"session.suspended":return`${B(n)} suspended`;case"session.woke":return`${B(n)} woke`;case"bead.created":return`${r} created bead ${n??""}`.trim();case"bead.updated":return`${r} updated bead ${n??""}`.trim();case"bead.closed":return`${r} closed bead ${n??""}`.trim();case"mail.delivered":return`${r} delivered mail`;case"mail.read":return`${r} read mail`;case"convoy.created":return`${r} created convoy ${n??""}`.trim();case"convoy.closed":return`${r} closed convoy ${n??""}`.trim();default:return a??n??e}}function Qe(e,t){return e?e.length<=t?e:`${e.slice(0,t-1)}…`:""}function ee(e){return typeof e!="number"||Number.isNaN(e)||e<=0?4:e}function Xt(e){switch(ee(e)){case 1:return"badge-red";case 2:return"badge-orange";case 3:return"badge-yellow";default:return"badge-muted"}}function ce(e){switch((e??"").toLowerCase()){case"open":case"running":case"ready":case"working":return"badge-green";case"in_progress":case"pending":case"stale":case"warning":return"badge-yellow";case"closed":case"stopped":return"badge-muted";case"error":case"failed":case"stuck":return"badge-red";default:return"badge-blue"}}async function oa(){var w,E,b;const e=S(),t=c("status-banner");if(!t)return;if(!e){await ca(t);return}da();const[n,a,r,i]=await Promise.all([g.GET("/v0/city/{cityName}/status",{params:{path:{cityName:e}}}),g.GET("/v0/city/{cityName}/sessions",{params:{path:{cityName:e},query:{state:"active",peek:!0}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{status:"open",limit:500}}}),g.GET("/v0/city/{cityName}/convoys",{params:{path:{cityName:e},query:{limit:200}}})]);if(n.error||!n.data){k(t),t.append(s("div",{class:"banner-error"},[`Status unavailable for ${e}`]));return}const o=((w=a.data)==null?void 0:w.items)??[],l=((E=r.data)==null?void 0:E.items)??[],d=((b=i.data)==null?void 0:b.items)??[];la(e,o);const p=o.filter(C=>!C.pool||!C.running||!C.last_active?!1:Date.now()-new Date(C.last_active).getTime()>=1800*1e3).length,f=l.filter(C=>C.assignee&&C.status!=="closed").length,u=l.filter(C=>ee(C.priority)<=2).length,y=o.filter(C=>!C.running).length,m=s("div",{class:"summary-stats"},[H(n.data.agents.running,"Agents"),H(n.data.work.in_progress,"Assigned"),H(n.data.work.open,"Beads"),H(d.length,"Convoys"),H(n.data.mail.unread,"Unread")]),h=s("div",{class:"summary-alerts"});Q(h,p>0,"alert-red",`${p} stuck`),Q(h,f>0,"alert-yellow",`${f} assigned`),Q(h,u>0,"alert-red",`${u} P1/P2`),Q(h,y>0,"alert-red",`${y} dead`),h.childNodes.length||h.append(s("span",{class:"alert-item alert-green"},["All clear"])),k(t),t.append(m,h)}async function ca(e){var u,y;ua();const[t,n]=await Promise.all([g.GET("/health"),g.GET("/v0/cities")]),a=t.data,r=((u=n.data)==null?void 0:u.items)??[],i=(a==null?void 0:a.cities_total)??r.length,o=(a==null?void 0:a.cities_running)??r.filter(m=>m.running===!0).length,l=Math.max(i-o,0),d=r.filter(m=>!!m.error).length;if(k(e),t.error&&n.error){e.append(s("div",{class:"banner-error"},["Supervisor status unavailable"]));return}const p=s("div",{class:"summary-stats"},[H(i,"🏙️ Cities"),H(o,"🟢 Running"),H(l,"⏸ Stopped"),H(fa(a==null?void 0:a.uptime_sec),"⏱ Uptime")]),f=s("div",{class:"summary-alerts"});Q(f,i===0,"alert-yellow","No registered cities"),Q(f,l>0,"alert-yellow",`${l} ${l===1?"city":"cities"} not running`),Q(f,d>0,"alert-red",`${d} ${d===1?"city":"cities"} reporting errors`),Q(f,!!(a!=null&&a.startup&&!a.startup.ready),"alert-yellow",`⏳ Startup: ${((y=a==null?void 0:a.startup)==null?void 0:y.phase)||"starting"}`),f.childNodes.length||f.append(s("span",{class:"alert-item alert-green"},["✓ Supervisor ready"])),e.append(p,f)}function H(e,t){return s("div",{class:"stat"},[s("span",{class:"stat-value"},[String(e??0)]),s("span",{class:"stat-label"},[t])])}function Q(e,t,n,a){t&&e.append(s("span",{class:`alert-item ${n}`},[a]))}function la(e,t){const n=c("scope-banner"),a=c("scope-badge"),r=c("scope-status");if(!n||!a||!r)return;const i=t.find(l=>!l.rig&&!l.pool);if(!i){n.classList.remove("attached"),n.classList.add("detached"),a.className="badge badge-muted",a.textContent="Detached",k(r),r.append(K("Scope",e),K("Overseer","none"));return}n.classList.remove("attached","detached"),n.classList.add(i.attached?"attached":"detached"),a.className=`badge ${i.attached?"badge-green":"badge-muted"}`,a.textContent=i.attached?"Attached":"Detached",k(r);const o=i.last_active?Date.now()-new Date(i.last_active).getTime()(e.client??le).sse.get({url:"/v0/city/{cityName}/events/stream",...e}),ya=e=>(e.client??le).sse.get({url:"/v0/city/{cityName}/session/{id}/stream",...e}),ma=e=>((e==null?void 0:e.client)??le).sse.get({url:"/v0/events/stream",...e});let re=0,rt=null;function ga(e){rt=e}function Yt(e){re=Math.max(0,e),document.body.dataset.pauseRefresh=re>0?"true":"false"}function F(){Yt(re+1)}function j(){const e=re>0;if(Yt(re-1),e&&re===0&&rt)try{rt()}catch(t){oe("ui","popPause listener threw",{error:String(t)})}}function bt(){return re>0}function Rt(e,t){const n=c("output-panel"),a=c("output-panel-cmd"),r=c("output-panel-content");!n||!a||!r||(a.textContent=e,r.textContent=t,n.classList.add("open"))}function Zt(){var e;(e=c("output-panel"))==null||e.classList.remove("open")}function v(e,t,n){const a=c("toast-container");if(!a)return;const r=document.createElement("div");r.className=`toast toast-${e}`,r.innerHTML=`${Ot(t)}
${Ot(n)}
`,a.append(r);const i=e==="error"?9e3:5e3;window.requestAnimationFrame(()=>{r.classList.add("show")}),window.setTimeout(()=>{r.classList.remove("show"),window.setTimeout(()=>{r.remove()},300)},i)}function P(e,t,n="Unexpected dashboard error"){const a=t instanceof Error?t.message:n;oe("ui",e,{error:t,fallbackMessage:n,message:a}),v("error",e,a)}function ba(){var e,t;document.addEventListener("click",n=>{const a=n.target,r=a==null?void 0:a.closest(".collapse-btn");if(r){const p=r.closest(".panel");p==null||p.classList.toggle("collapsed");return}const i=a==null?void 0:a.closest(".expand-btn");if(!i)return;const o=i.closest(".panel");if(!o)return;const l=o.classList.contains("expanded"),d=!!document.querySelector(".panel.expanded");if(document.querySelectorAll(".panel.expanded").forEach(p=>{p.classList.remove("expanded");const f=p.querySelector(".expand-btn");f&&(f.textContent="Expand")}),l){j();return}o.classList.add("expanded"),i.textContent="✕ Close",d||F()}),document.addEventListener("keydown",n=>{if(n.key!=="Escape")return;const a=document.querySelector(".panel.expanded");if(a){a.classList.remove("expanded");const r=a.querySelector(".expand-btn");r&&(r.textContent="Expand"),j()}}),(e=c("output-close-btn"))==null||e.addEventListener("click",()=>Zt()),(t=c("output-copy-btn"))==null||t.addEventListener("click",async()=>{var a;const n=((a=c("output-panel-content"))==null?void 0:a.textContent)??"";try{await navigator.clipboard.writeText(n),v("success","Copied","Output copied to clipboard")}catch{v("error","Copy failed","Clipboard write was rejected")}})}function Ot(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function en(e){return typeof e=="object"&&e!==null}function tn(e){return en(e)&&typeof e.timestamp=="string"}function nn(e){return en(e)&&typeof e.actor=="string"&&typeof e.seq=="number"&&typeof e.ts=="string"&&typeof e.type=="string"}function ha(e){return nn(e)}function va(e){return nn(e)&&typeof e.city=="string"}const qt=[1e3,2e3,4e3,8e3,15e3],wa=15e3;function an(e){return e{var o;let r=0,i=!1;for(;!n.signal.aborted;){try{const{stream:d}=await ma({client:le,signal:n.signal,onSseEvent:p=>{var u;r=0,i=!1,(u=t==null?void 0:t.onStatus)==null||u.call(t,"live");const f=p.event??"tagged_event";if(f==="heartbeat"){if(!tn(p.data)){P("Invalid supervisor heartbeat frame",p);return}e({event:"heartbeat",id:p.id,data:p.data});return}if(f==="tagged_event"){if(!va(p.data)){P("Invalid supervisor event frame",p);return}e({event:"tagged_event",id:p.id,data:p.data});return}P(`Unexpected supervisor SSE event: ${f}`,p)}});for await(const p of d);if(n.signal.aborted)break}catch(d){if(n.signal.aborted)return;i||(P("Supervisor event stream failed",d),i=!0)}(o=t==null?void 0:t.onStatus)==null||o.call(t,"reconnecting");const l=an(r);r+=1,await sn(l,n.signal)}})(),{close:()=>n.abort()}}function Ea(e,t,n){var r;const a=new AbortController;return(r=n==null?void 0:n.onStatus)==null||r.call(n,"connecting"),(async()=>{var l;let i=0,o=!1;for(;!a.signal.aborted;){try{const{stream:p}=await pa({client:le,path:{cityName:e},signal:a.signal,onSseEvent:f=>{var m;i=0,o=!1,(m=n==null?void 0:n.onStatus)==null||m.call(n,"live");const u=f.event??"event",y=f.id!==void 0?String(f.id):void 0;if(u==="heartbeat"){if(!tn(f.data)){P("Invalid city heartbeat frame",f);return}t({event:"heartbeat",id:y,data:f.data});return}if(u==="event"){if(!ha(f.data)){P("Invalid city event frame",f);return}t({event:"event",id:y,data:f.data});return}P(`Unexpected city SSE event: ${u}`,f)}});for await(const f of p);if(a.signal.aborted)break}catch(p){if(a.signal.aborted)return;o||(P("City event stream failed",p),o=!0)}(l=n==null?void 0:n.onStatus)==null||l.call(n,"reconnecting");const d=an(i);i+=1,await sn(d,a.signal)}})(),{close:()=>a.abort()}}async function sn(e,t){if(!t.aborted)return new Promise(n=>{const a=setTimeout(()=>{t.removeEventListener("abort",r),n()},e),r=()=>{clearTimeout(a),t.removeEventListener("abort",r),n()};t.addEventListener("abort",r)})}function Ca(e,t,n){const a=new AbortController;return(async()=>{try{const{stream:r}=await ya({client:le,path:{cityName:e,id:t},signal:a.signal,onSseEvent:i=>{if(i.data===void 0){P("Session frame missing data",i);return}n({id:i.id!==void 0?String(i.id):void 0,type:i.event??"message",data:i.data})}});for await(const i of r);}catch(r){a.signal.aborted||P("Session stream failed",r)}})(),{close:()=>a.abort()}}function ka(e){return e.event==="heartbeat"?"heartbeat":e.data.type}let Te=null,ge="",X="",Pe=0;async function Na(){const e=S();if(!e){$a();return}const t=c("crew-loading"),n=c("crew-table"),a=c("crew-empty"),r=c("crew-tbody"),i=c("rigged-body"),o=c("pooled-body");if(!t||!n||!a||!r||!i||!o)return;it("No crew configured"),t.style.display="block",n.style.display="none",a.style.display="none",k(r);const{data:l,error:d}=await g.GET("/v0/city/{cityName}/sessions",{params:{path:{cityName:e},query:{state:"active",peek:!0}}});if(d||!(l!=null&&l.items)){t.textContent="Failed to load crew",ve(i,"No rigged agents"),ve(o,"No pooled agents");return}const p=l.items,f=await Promise.all(p.map(async m=>{var w;return!!((w=(await g.GET("/v0/city/{cityName}/session/{id}/pending",{params:{path:{cityName:e,id:m.id}}})).data)!=null&&w.pending)})),u=new Map;await Promise.all(p.map(async m=>{var w;if(!m.active_bead||u.has(m.active_bead))return;const h=await g.GET("/v0/city/{cityName}/bead/{id}",{params:{path:{cityName:e,id:m.active_bead}}});u.set(m.active_bead,(w=h.data)!=null&&w.id?h.data.title??h.data.id:m.active_bead)}));const y=p;y.forEach((m,h)=>{const w=La(m,f[h]??!1),E=m.active_bead?Qe(u.get(m.active_bead)??m.active_bead,24):"—",b=s("tr",{},[s("td",{},[m.template]),s("td",{},[m.rig??"city"]),s("td",{},[s("span",{class:`badge ${ce(w)}`},[w])]),s("td",{},[E]),s("td",{class:_e(m.last_active).colorClass?`activity-${_e(m.last_active).colorClass}`:""},[s("span",{class:"activity-dot"}),` ${_e(m.last_active).display}`]),s("td",{},[s("span",{class:`badge ${m.attached?"badge-green":"badge-muted"}`},[m.attached?"Attached":"Detached"])]),s("td",{},[xa(m.template)," ",rn(m.id,m.template)])]);r.append(b)}),c("crew-count").textContent=String(y.length),t.style.display="none",y.length>0?n.style.display="table":(it("No crew configured"),a.style.display="block"),Ta(p,u),Aa(p)}function $a(){const e=c("crew-loading"),t=c("crew-table"),n=c("crew-empty"),a=c("crew-tbody"),r=c("rigged-body"),i=c("pooled-body");!e||!t||!n||!a||!r||!i||(je(),c("crew-count").textContent="0",c("rigged-count").textContent="0",c("pooled-count").textContent="0",e.style.display="none",t.style.display="none",n.style.display="block",it("Select a city to view crew"),k(a),ve(r,"Select a city to view rigged agents"),ve(i,"Select a city to view pooled agents"))}function it(e){var t,n;(n=(t=c("crew-empty"))==null?void 0:t.querySelector("p"))==null||n.replaceChildren(document.createTextNode(e))}function La(e,t){return t?"questions":e.active_bead?"spinning":e.running?"idle":"finished"}function xa(e){const t=s("button",{class:"attach-btn",type:"button"},["📎 Attach"]);return t.addEventListener("click",async()=>{const n=`gc agent attach ${e}`;try{await navigator.clipboard.writeText(n),v("success","Attach command copied",n)}catch{v("error","Copy failed",n)}}),t}function rn(e,t){const n=s("button",{class:"agent-log-link",type:"button","data-session-id":e},[t]);return n.addEventListener("click",()=>{Oa(e,t)}),n}function Ta(e,t){const n=c("rigged-body"),a=c("rigged-count");if(!n||!a)return;const r=e.filter(o=>o.rig&&o.pool);if(a.textContent=String(r.length),r.length===0){ve(n,"No rigged agents");return}const i=s("tbody");r.forEach(o=>{const l=_e(o.last_active),d=o.active_bead?l.colorClass==="red"?"Stuck":l.colorClass==="yellow"?"Stale":"Working":"Idle";i.append(s("tr",{class:`rigged-${d.toLowerCase()}`},[s("td",{},[rn(o.id,o.template)]),s("td",{},[s("span",{class:"badge badge-muted"},[o.pool??"pool"])]),s("td",{},[o.rig??"city"]),s("td",{class:"rigged-issue"},[o.active_bead?`${o.active_bead} ${t.get(o.active_bead)??""}`.trim():"—"]),s("td",{},[s("span",{class:`badge ${ce(d)}`},[d])]),s("td",{class:`activity-${l.colorClass}`},[s("span",{class:"activity-dot"}),` ${l.display}`])]))}),k(n),n.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Agent"]),s("th",{},["Pool"]),s("th",{},["Rig"]),s("th",{},["Working On"]),s("th",{},["Status"]),s("th",{},["Activity"])])]),i]))}function Aa(e){const t=c("pooled-body"),n=c("pooled-count");if(!t||!n)return;const a=e.filter(i=>!i.rig&&i.pool);if(n.textContent=String(a.length),a.length===0){ve(t,"No pooled agents");return}const r=s("tbody");a.forEach(i=>{r.append(s("tr",{},[s("td",{},[i.template]),s("td",{},[s("span",{class:`badge ${i.active_bead?"badge-yellow":"badge-green"}`},[i.active_bead?"Working":"Idle"])]),s("td",{class:"status-hint"},[Qe(i.last_output,80)||"—"]),s("td",{},[D(i.last_active)])]))}),k(t),t.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Agent"]),s("th",{},["State"]),s("th",{},["Work"]),s("th",{},["Activity"])])]),r]))}function ve(e,t){k(e),e.append(s("div",{class:"empty-state"},[s("p",{},[t])]))}function Ra(){var e,t;(e=c("log-drawer-close-btn"))==null||e.addEventListener("click",()=>je()),(t=c("log-drawer-older-btn"))==null||t.addEventListener("click",()=>{he("crew","Load older transcript clicked",{hasCursor:X!=="",sessionID:ge}),!(!ge||!X)&&cn(ge,!0)})}async function Oa(e,t){const n=c("agent-log-drawer"),a=c("log-drawer-agent-name"),r=c("log-drawer-messages"),i=c("log-drawer-loading");if(!n||!a||!r||!i)return;if(ge===e&&n.style.display!=="none"){je();return}je(),ge=e,X="",Pe=0,a.textContent=t,k(r),r.append(i),i.style.display="block",n.style.display="block",F(),await cn(e,!1);const o=S();o&&(Te=Ca(o,e,l=>qa(l)))}function je(){Te==null||Te.close(),Te=null,ge="",X="";const e=c("agent-log-drawer");e&&e.style.display!=="none"&&(e.style.display="none",j())}function on(){je()}async function cn(e,t){var p,f,u,y,m;const n=S(),a=c("log-drawer-messages"),r=c("log-drawer-loading"),i=c("log-drawer-older-btn"),o=c("log-drawer-count");if(!n||!a||!r||!i||!o)return;r.style.display="block";const l=await g.GET("/v0/city/{cityName}/session/{id}/transcript",{params:{path:{cityName:n,id:e},query:{tail:String(t?50:25),before:t?X:void 0}}});if(r.style.display="none",l.error||!l.data){v("error","Transcript failed",((p=l.error)==null?void 0:p.detail)??"Could not load transcript");return}const d=document.createDocumentFragment();for(const h of l.data.turns??[])d.append(ln(h.role,h.text,h.timestamp)),Pe+=1;t?a.prepend(d):(k(a),a.append(d)),a.append(r),r.style.display="none",o.textContent=String(Pe),X=((f=l.data.pagination)==null?void 0:f.truncated_before_message)??"",i.style.display=(u=l.data.pagination)!=null&&u.has_older_messages&&X?"inline-flex":"none",he("crew","Transcript loaded",{hasOlderMessages:((y=l.data.pagination)==null?void 0:y.has_older_messages)??!1,nextBeforeCursor:X,prepend:t,sessionID:e,turnCount:((m=l.data.turns)==null?void 0:m.length)??0})}function qa(e){var r;const t=c("log-drawer-messages");if(!t)return;const n=e.data;if(e.type!=="message"||!((r=n==null?void 0:n.data)!=null&&r.message))return;t.append(ln(n.data.message.role??"agent",n.data.message.text??"",n.data.message.timestamp)),Pe+=1,c("log-drawer-count").textContent=String(Pe);const a=c("log-drawer-body");a&&(a.scrollTop=a.scrollHeight)}function ln(e,t,n){return s("div",{class:"log-msg"},[s("div",{class:"log-msg-header"},[s("span",{class:`log-msg-type log-msg-type-${_a(e)}`},[e]),s("span",{class:"log-msg-time"},[D(n)])]),s("div",{class:"log-msg-body"},[t])])}function _a(e){switch((e??"").toLowerCase()){case"assistant":case"agent":return"assistant";case"system":return"system";case"result":return"result";default:return"user"}}const Pa=3e4,ot=new Map,Ae=new Map;async function Xe(e=!1){const t=S(),n=Date.now(),a=ot.get(t);if(!e&&a&&n-a.fetchedAt(ot.set(t,o),Ae.delete(t),o)).catch(o=>{throw Ae.delete(t),o});return Ae.set(t,i),i}async function ja(e){var l,d,p,f,u,y,m,h,w,E,b,C;const t={agents:[],rigs:[],sessions:[],beads:[],mail:[],fetchedAt:Date.now()};if(!e)return t;const[n,a,r,i]=await Promise.all([g.GET("/v0/city/{cityName}/config",{params:{path:{cityName:e}}}),g.GET("/v0/city/{cityName}/rigs",{params:{path:{cityName:e}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{status:"open"}}}),g.GET("/v0/city/{cityName}/mail",{params:{path:{cityName:e}}})]);n.error&&Ve("options","Config options request failed",{city:e,detail:n.error.detail??null});const o=(((l=n.data)==null?void 0:l.agents)??[]).map(N=>({id:N.name??"",label:N.name??"",recipient:N.name??""})).filter(N=>N.recipient!=="");return he("options","Fetched options",{agentOptions:o.map(N=>N.recipient),beads:((p=(d=r.data)==null?void 0:d.items)==null?void 0:p.length)??0,city:e,configAgents:((u=(f=n.data)==null?void 0:f.agents)==null?void 0:u.length)??0,mail:((m=(y=i.data)==null?void 0:y.items)==null?void 0:m.length)??0,rigs:((w=(h=a.data)==null?void 0:h.items)==null?void 0:w.length)??0}),{agents:[...new Set(o.map(N=>N.recipient))].sort(),rigs:(((E=a.data)==null?void 0:E.items)??[]).map(N=>N.name??"").filter(Boolean),sessions:o,beads:(((b=r.data)==null?void 0:b.items)??[]).map(N=>({id:N.id??"",title:N.title??""})),mail:(((C=i.data)==null?void 0:C.items)??[]).map(N=>({id:N.id??"",subject:N.subject??""})),fetchedAt:Date.now()}}function Ia(){ot.clear(),Ae.clear()}let Re=null,Oe=null;function Ba(){var e,t,n,a,r,i,o,l,d,p;(e=c("action-modal-close-btn"))==null||e.addEventListener("click",()=>Ne(null)),(t=c("action-modal-cancel-btn"))==null||t.addEventListener("click",()=>Ne(null)),(a=(n=c("action-modal"))==null?void 0:n.querySelector(".modal-backdrop"))==null||a.addEventListener("click",()=>Ne(null)),(r=c("action-form"))==null||r.addEventListener("submit",f=>{var h,w,E;f.preventDefault();const u=((h=c("action-bead-id"))==null?void 0:h.value.trim())??"",y=((w=c("action-target"))==null?void 0:w.value.trim())??"",m=((E=c("action-rig"))==null?void 0:E.value.trim())??"";!u||!y||Ne({beadID:u,rig:m,target:y})}),(i=c("confirm-modal-close-btn"))==null||i.addEventListener("click",()=>$e(!1)),(o=c("confirm-modal-cancel-btn"))==null||o.addEventListener("click",()=>$e(!1)),(l=c("confirm-modal-confirm-btn"))==null||l.addEventListener("click",()=>$e(!0)),(p=(d=c("confirm-modal"))==null?void 0:d.querySelector(".modal-backdrop"))==null||p.addEventListener("click",()=>$e(!1)),document.addEventListener("keydown",f=>{if(f.key==="Escape"){if(we("action-modal")){Ne(null);return}we("confirm-modal")&&$e(!1)}})}async function ht(e){const t=c("action-modal"),n=c("action-form"),a=c("action-modal-title"),r=c("action-modal-submit-btn"),i=c("action-bead-group"),o=c("action-bead-id"),l=c("action-bead-hint"),d=c("action-target"),p=c("action-target-label"),f=c("action-rig-group"),u=c("action-rig"),y=c("action-modal-help"),m=c("action-target-list"),h=c("action-rig-list");if(!t||!n||!a||!r||!i||!o||!l||!d||!p||!f||!u||!y||!m||!h)return P("Action modal unavailable",new Error("missing action modal DOM")),null;const w=await Xe();return _t(m,w.agents),_t(h,w.rigs),a.textContent=e.title,r.textContent=Ua(e.mode),p.textContent=e.mode==="reassign"?"Assignee":"Target agent or pool",y.textContent=Da(e.mode),o.value=e.beadID??"",o.readOnly=!!e.beadID,i.classList.toggle("readonly",o.readOnly),l.textContent=e.beadLabel??"",d.value=e.initialTarget??"",u.value=e.initialRig??"",f.hidden=e.mode==="reassign",u.disabled=e.mode==="reassign",we("action-modal")||F(),t.style.display="flex",window.setTimeout(()=>{if(e.beadID){d.focus();return}o.focus()},0),new Promise(E=>{Re=E})}async function Ma(e){const t=c("confirm-modal"),n=c("confirm-modal-title"),a=c("confirm-modal-body"),r=c("confirm-modal-confirm-btn");return!t||!n||!a||!r?(P("Confirm modal unavailable",new Error("missing confirm modal DOM")),!1):(n.textContent=e.title,a.textContent=e.body,r.textContent=e.confirmLabel,we("confirm-modal")||F(),t.style.display="flex",new Promise(i=>{Oe=i}))}function _t(e,t){k(e),t.forEach(n=>{e.append(s("option",{value:n}))})}function Ua(e){switch(e){case"assign":return"Assign";case"reassign":return"Reassign";default:return"Sling"}}function Da(e){switch(e){case"assign":return"Launch a bead directly to a target, with an optional rig override.";case"reassign":return"Pick a new assignee from the active city sessions or type one manually.";default:return"Dispatch this bead to a target, with an optional rig constraint."}}function Ne(e){const t=c("action-modal"),n=c("action-form");if(!t||!n)return;const a=we("action-modal");t.style.display="none",n.reset(),c("action-rig").disabled=!1,c("action-bead-id").readOnly=!1,a&&j(),Re==null||Re(e),Re=null}function $e(e){const t=c("confirm-modal");if(!t)return;const n=we("confirm-modal");t.style.display="none",n&&j(),Oe==null||Oe(e),Oe=null}function we(e){var t;return((t=c(e))==null?void 0:t.style.display)==="flex"}let Fe=[],ct="ready",Se="all",Ye="";async function de(){var o,l,d,p;const e=S(),t=c("issues-list");if(!t)return;if(!e){za();return}const[n,a,r]=await Promise.all([g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{status:"open",limit:500}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{status:"in_progress",limit:500}}}),Xe(!0)]);if(n.error&&a.error||!((o=n.data)!=null&&o.items)&&!((l=a.data)!=null&&l.items)){k(t),t.append(s("div",{class:"panel-error"},["Could not load beads."]));return}Fe=[...((d=n.data)==null?void 0:d.items)??[],...((p=a.data)==null?void 0:p.items)??[]].filter(f=>!Wa(f)).sort((f,u)=>{const y=ee(f.priority),m=ee(u.priority);return y!==m?y-m:(u.created_at??"").localeCompare(f.created_at??"")}),c("issues-count").textContent=String(Fe.length);const i=c("rig-filter-tabs");i&&(k(i),i.append(lt("all",Se==="all")),r.rigs.forEach(f=>i.append(lt(f,Se===f)))),vt()}function za(){const e=c("issues-list"),t=c("rig-filter-tabs"),n=c("issue-detail");if(!e||!t||!n)return;me();const a=n.style.display==="block";n.style.display="none",e.style.display="block",k(e),e.append(s("div",{class:"empty-state"},[s("p",{},["Select a city to view beads"])])),k(t),Se="all",Ye="",Fe=[],t.append(lt("all",!0)),c("issues-count").textContent="0",a&&j()}function vt(){const e=c("issues-list");if(!e)return;k(e);const t=Fe.filter(a=>{const r=a.assignee?"progress":"ready",i=ct==="all"||ct===r,o=Se==="all"||nt(a)===Se;return i&&o});if(t.length===0){e.append(s("div",{class:"empty-state"},[s("p",{},["No beads"])]));return}const n=s("tbody");t.forEach(a=>{const r=s("tr",{class:`issue-row priority-${ee(a.priority)}`,"data-issue-id":a.id??"","data-status":a.assignee?"progress":"ready","data-rig":nt(a)},[s("td",{},[s("span",{class:`badge ${Xt(a.priority)}`},[`P${ee(a.priority)}`])]),s("td",{},[s("span",{class:"issue-id"},[a.id??""])]),s("td",{class:"issue-title"},[Qe(a.title??a.id??"",80)]),s("td",{class:"issue-rig"},[nt(a)]),s("td",{class:"issue-status"},[a.assignee?s("span",{class:"badge badge-blue",title:a.assignee},[a.assignee]):s("span",{class:"badge badge-green"},["Ready"])]),s("td",{class:"issue-age"},[D(a.created_at)]),s("td",{},[ts(a.id??"")])]);r.addEventListener("click",i=>{i.target.closest(".sling-btn")||a.id&&ue(a.id)}),n.append(r)}),e.append(s("table",{id:"work-table"},[s("thead",{},[s("tr",{},[s("th",{},["Pri"]),s("th",{},["ID"]),s("th",{},["Title"]),s("th",{},["Rig"]),s("th",{},["Status"]),s("th",{},["Age"]),s("th",{},["Actions"])])]),n]))}function lt(e,t){const n=s("button",{class:`rig-btn${t?" active":""}`,"data-rig":e},[e==="all"?"All":e]);return n.addEventListener("click",()=>{Se=e,document.querySelectorAll(".rig-btn").forEach(a=>a.classList.remove("active")),n.classList.add("active"),vt()}),n}function nt(e){var t;return((t=e.id)==null?void 0:t.split("-")[0])??"city"}function Wa(e){return(e.issue_type??"").toLowerCase()==="convoy"?!0:(e.labels??[]).some(t=>t.startsWith("gc:queue")||t.startsWith("gc:message"))}function Ga(){var e,t,n,a,r,i,o;document.querySelectorAll(".tab-btn").forEach(l=>{l.addEventListener("click",d=>{const p=d.currentTarget;ct=p.dataset.tab??"ready",document.querySelectorAll(".tab-btn").forEach(f=>f.classList.remove("active")),p.classList.add("active"),vt()})}),(e=c("new-issue-btn"))==null||e.addEventListener("click",()=>dn()),(t=c("issue-modal-close-btn"))==null||t.addEventListener("click",()=>me()),(n=c("issue-modal-cancel-btn"))==null||n.addEventListener("click",()=>me()),(r=(a=c("issue-modal"))==null?void 0:a.querySelector(".modal-backdrop"))==null||r.addEventListener("click",()=>me()),(i=c("issue-form"))==null||i.addEventListener("submit",l=>{l.preventDefault(),Fa()}),(o=c("issue-back-btn"))==null||o.addEventListener("click",()=>Qa()),document.addEventListener("keydown",l=>{var d;l.key==="Escape"&&((d=c("issue-modal"))==null?void 0:d.style.display)==="block"&&me()})}function dn(){var t,n,a;if(!S()){v("info","No city selected","Select a city to create a bead");return}const e=c("issue-modal");e&&(e.style.display!=="block"&&F(),e.style.display="block",(n=(t=c("issues-panel"))==null?void 0:t.scrollIntoView)==null||n.call(t,{behavior:"smooth",block:"center"}),(a=c("issue-title"))==null||a.focus())}function me(){var n;const e=c("issue-modal");if(!e)return;const t=e.style.display==="block";e.style.display="none",(n=c("issue-form"))==null||n.reset(),t&&j()}async function Fa(){var r,i,o;const e=((r=c("issue-title"))==null?void 0:r.value.trim())??"",t=((i=c("issue-description"))==null?void 0:i.value.trim())??"",n=Number(((o=c("issue-priority"))==null?void 0:o.value)??"2");if(!e)return;const a=await ns({title:e,description:t,priority:n});if(!a.ok){v("error","Create failed",a.error??"Could not create issue");return}v("success","Issue created",e),me(),await de()}async function ue(e){var l,d,p;const t=S();if(!t)return;Ye=e,((l=c("issue-detail"))==null?void 0:l.style.display)!=="block"&&F(),c("issues-list").style.display="none",c("issue-detail").style.display="block";const[n,a,r]=await Promise.all([g.GET("/v0/city/{cityName}/bead/{id}",{params:{path:{cityName:t,id:e}}}),g.GET("/v0/city/{cityName}/bead/{id}/deps",{params:{path:{cityName:t,id:e}}}),Xe()]);if(n.error||!n.data){v("error","Issue failed",((d=n.error)==null?void 0:d.detail)??"Could not load bead");return}const i=n.data;c("issue-detail-id").textContent=i.id??e,c("issue-detail-title-text").textContent=i.title??e,c("issue-detail-description").textContent=i.description||"(no description)";const o=c("issue-detail-priority");o.className=`badge ${Xt(i.priority)}`,o.textContent=`P${ee(i.priority)}`,c("issue-detail-status").textContent=i.status??"open",c("issue-detail-status").className=`issue-status ${i.status??"open"}`,c("issue-detail-type").textContent=i.issue_type?`Type: ${i.issue_type}`:"",c("issue-detail-owner").textContent=i.assignee?`Owner: ${i.assignee}`:"Owner: unassigned",c("issue-detail-created").textContent=i.created_at?`Created: ${D(i.created_at)}`:"",Ja(i,r.agents),Ha(((p=a.data)==null?void 0:p.children)??[])}function Ha(e){const t=c("issue-detail-deps"),n=c("issue-detail-depends-on"),a=c("issue-detail-blocks-section"),r=c("issue-detail-blocks");if(!(!t||!n||!a||!r)){if(k(n),k(r),e.length===0){t.style.display="none",a.style.display="none";return}t.style.display="block",e.forEach(i=>{const o=s("span",{class:"issue-dep-item","data-issue-id":i.id??""},[`→ ${i.id??""}`]);o.addEventListener("click",()=>{i.id&&ue(i.id)}),n.append(o)}),a.style.display="none"}}function Ja(e,t){const n=c("issue-detail-actions");if(!n||!e.id)return;k(n);const a=s("div",{class:"issue-actions-bar"}),r=e.status==="closed"?at("↺ Reopen","reopen",()=>void Ya(e.id)):at("✓ Close","close",()=>void Xa(e.id));a.append(r),e.status!=="closed"&&a.append(at("🚚 Sling","sling",()=>void un(e.id)));const i=s("div",{class:"issue-action-group"},[s("label",{class:"issue-action-label"},["Priority"]),Va(e.id,e.priority)]),o=s("div",{class:"issue-action-group"},[s("label",{class:"issue-action-label"},["Assign"]),Ka(e.id,e.assignee,t)]);n.append(a,i,o)}function at(e,t,n){const a=s("button",{class:`issue-action-btn ${t}`,type:"button"},[e]);return a.addEventListener("click",n),a}function Va(e,t){const n=s("select",{class:"issue-action-select",id:"issue-action-priority"});return[1,2,3,4].forEach(a=>{const r=s("option",{value:a,selected:ee(t)===a},[`P${a}`]);n.append(r)}),n.addEventListener("change",()=>{Za(e,Number(n.value))}),n}function Ka(e,t,n){const a=s("select",{class:"issue-action-select",id:"issue-action-assignee"});return a.append(s("option",{value:""},["Unassigned"])),n.forEach(r=>{a.append(s("option",{value:r,selected:t===r},[r]))}),a.addEventListener("change",()=>{es(e,a.value)}),a}function Qa(){const e=c("issue-detail"),t=(e==null?void 0:e.style.display)==="block";e.style.display="none",c("issues-list").style.display="block",Ye="",t&&j()}async function Xa(e){const t=S();if(!t)return;const n=await g.POST("/v0/city/{cityName}/bead/{id}/close",{params:{path:{cityName:t,id:e},header:T}});if(n.error){v("error","Close failed",n.error.detail??"Could not close issue");return}v("success","Closed",e),await de(),await ue(e)}async function Ya(e){const t=S();if(!t)return;const n=await g.POST("/v0/city/{cityName}/bead/{id}/reopen",{params:{path:{cityName:t,id:e},header:T}});if(n.error){v("error","Reopen failed",n.error.detail??"Could not reopen issue");return}v("success","Reopened",e),await de(),await ue(e)}async function Za(e,t){const n=S();if(!n)return;const a=await g.POST("/v0/city/{cityName}/bead/{id}/update",{params:{path:{cityName:n,id:e},header:T},body:{priority:t}});if(a.error){v("error","Priority failed",a.error.detail??"Could not update priority");return}v("success","Priority updated",`${e} → P${t}`),await de(),await ue(e)}async function es(e,t){const n=S();if(!n)return;const a=await g.POST("/v0/city/{cityName}/bead/{id}/assign",{params:{path:{cityName:n,id:e},header:T},body:{assignee:t}});if(a.error){v("error","Assign failed",a.error.detail??"Could not update assignee");return}v("success","Assignment updated",t||"Unassigned"),await de(),await ue(e)}async function un(e){const t=S();if(!t)return;const n=await ht({beadID:e,beadLabel:e,mode:"sling",title:"Sling Bead"});if(!n)return;const a=await g.POST("/v0/city/{cityName}/sling",{params:{path:{cityName:t},header:T},body:{bead:e,target:n.target,rig:n.rig||void 0}});if(a.error){v("error","Sling failed",a.error.detail??"Could not sling issue");return}v("success","Work assigned",`${e} → ${n.target}`),await de(),Ye===e&&await ue(e)}function ts(e){const t=s("button",{class:"sling-btn",type:"button","data-bead-id":e},["Sling"]);return t.addEventListener("click",n=>{n.stopPropagation(),un(e)}),t}async function ns(e){const t=S();if(!t)return{ok:!1,error:"no city selected"};const{error:n}=await g.POST("/v0/city/{cityName}/beads",{params:{path:{cityName:t},header:T},body:{title:e.title,description:e.description,rig:e.rig,priority:e.priority,assignee:e.assignee}});return n?{ok:!1,error:n.detail??n.title??"create failed"}:{ok:!0}}let U="inbox",qe=[],L=null;async function Ue(){const e=S(),t=c("mail-loading"),n=c("mail-threads"),a=c("mail-empty"),r=c("mail-all");if(!t||!n||!a||!r)return;if(!e){as();return}wt("No mail in inbox"),t.style.display="block",n.style.display="none",a.style.display="none";const{data:i,error:o}=await g.GET("/v0/city/{cityName}/mail",{params:{path:{cityName:e},query:{status:"all",limit:200}}});if(t.style.display="none",o||!(i!=null&&i.items)){k(n),n.append(s("div",{class:"panel-error"},["Could not load mail."])),n.style.display="block";return}qe=[...i.items].sort((l,d)=>(d.created_at??"").localeCompare(l.created_at??"")),c("mail-count").textContent=String(qe.length),ss(qe),rs(qe),cs()}function as(){const e=c("mail-loading"),t=c("mail-threads"),n=c("mail-empty"),a=c("mail-all");if(!e||!t||!n||!a)return;ie()?(z(U),j()):z(U),L=null,qe=[],c("mail-count").textContent="0",e.style.display="none",k(t),k(a),t.style.display="none",wt("Select a city to view mail"),n.style.display=U==="inbox"?"block":"none",a.append(s("div",{class:"empty-state"},[s("p",{},["Select a city to view mail traffic"])]))}function wt(e){var t,n;(n=(t=c("mail-empty"))==null?void 0:t.querySelector("p"))==null||n.replaceChildren(document.createTextNode(e))}function ss(e){const t=c("mail-threads"),n=c("mail-empty");if(!t||!n)return;const a=ms(e);if(k(t),a.length===0){t.style.display="none",wt("No mail in inbox"),n.style.display="block";return}n.style.display="none",a.forEach(r=>{const i=r.messages[r.messages.length-1],o=(i.body??"").trim().slice(0,60),l=s("div",{class:`mail-thread${r.unreadCount>0?" mail-thread-unread":""}`},[s("div",{class:"mail-thread-header"},[s("div",{class:"mail-thread-left"},[s("span",{class:"mail-from"},[B(i.from)])]),s("div",{class:"mail-thread-center"},[s("span",{class:"mail-subject"},[r.subject||"(no subject)"]),o?s("span",{class:"mail-thread-preview"},[` — ${o}`]):null]),s("div",{class:"mail-thread-right"},[s("span",{class:"mail-time"},[gt(i.created_at)]),r.unreadCount>0?s("span",{class:"badge badge-unread"},[`${r.unreadCount} unread`]):null])])]);l.addEventListener("click",()=>{is(r.id)}),t.append(l)}),t.style.display=U==="inbox"?"block":"none"}function rs(e){const t=c("mail-all");if(!t)return;if(k(t),e.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No mail traffic"])]));return}const n=s("tbody");e.forEach(a=>{const r=s("tr",{class:`mail-row${a.read?"":" mail-unread"}`},[s("td",{class:"mail-from"},[B(a.from)]),s("td",{class:"mail-to"},[B(a.to)]),s("td",{},[s("span",{class:"mail-subject"},[a.subject??"(no subject)"])]),s("td",{class:"mail-time"},[D(a.created_at)])]);r.addEventListener("click",()=>{a.id&&os(a.id)}),n.append(r)}),t.append(s("table",{class:"mail-all-table"},[s("thead",{},[s("tr",{},[s("th",{},["From"]),s("th",{},["To"]),s("th",{},["Subject"]),s("th",{},["Time"])])]),n])),t.style.display=U==="all"?"block":"none"}async function is(e){var i,o;const t=S();if(!t)return;const n=await g.GET("/v0/city/{cityName}/mail/thread/{id}",{params:{path:{cityName:t,id:e}}});if(n.error||!((i=n.data)!=null&&i.items)||n.data.items.length===0){v("error","Thread failed",((o=n.error)==null?void 0:o.detail)??"Could not load mail thread");return}const a=n.data.items,r=a[a.length-1]??a[0];L=r,fn(r,a)}async function os(e){var a;const t=S();if(!t)return;const n=await g.GET("/v0/city/{cityName}/mail/{id}",{params:{path:{cityName:t,id:e}}});if(n.error||!n.data){v("error","Message failed",((a=n.error)==null?void 0:a.detail)??"Could not load message");return}L=n.data,await g.POST("/v0/city/{cityName}/mail/{id}/read",{params:{path:{cityName:t,id:e},header:T}}),L.read=!0,fn(L,[L]),Ue()}function fn(e,t){const n=ie();c("mail-detail-subject").textContent=e.subject??"(no subject)",c("mail-detail-from").textContent=B(e.from),c("mail-detail-time").textContent=D(e.created_at);const a=c("mail-detail-body");a&&(k(a),t.forEach((r,i)=>{i>0&&a.append(s("hr")),a.append(s("div",{class:"mail-thread-msg-header"},[s("span",{class:"mail-from"},[B(r.from)]),s("span",{class:"mail-time"},[D(r.created_at)])]),s("div",{class:"mail-thread-msg-subject"},[r.subject??"(no subject)"]),s("pre",{},[r.body??""]))})),pn(),z("detail"),yn("mail-detail"),n||F()}function z(e){const t=c("mail-list"),n=c("mail-all"),a=c("mail-detail"),r=c("mail-compose");!t||!n||!a||!r||(t.style.display=e==="inbox"?"block":"none",n.style.display=e==="all"?"block":"none",a.style.display=e==="detail"?"block":"none",r.style.display=e==="compose"?"block":"none")}function cs(){var e,t;((e=c("mail-compose"))==null?void 0:e.style.display)==="block"||((t=c("mail-detail"))==null?void 0:t.style.display)==="block"||z(U)}function ls(){var e,t,n,a,r,i,o,l;document.querySelectorAll(".mail-tab").forEach(d=>{d.addEventListener("click",p=>{const f=p.currentTarget;U=f.dataset.tab??"inbox",document.querySelectorAll(".mail-tab").forEach(u=>u.classList.remove("active")),f.classList.add("active"),z(U)})}),(e=c("mail-back-btn"))==null||e.addEventListener("click",()=>{const d=ie();z(U),L=null,d&&j()}),(t=c("compose-mail-btn"))==null||t.addEventListener("click",()=>{dt()}),(n=c("compose-back-btn"))==null||n.addEventListener("click",()=>{const d=!!L,p=ie();z(d?"detail":U),p&&!d&&j()}),(a=c("compose-cancel-btn"))==null||a.addEventListener("click",()=>{const d=ie();z(U),d&&j()}),(r=c("mail-reply-btn"))==null||r.addEventListener("click",()=>{L!=null&&L.id&&dt(L)}),(i=c("mail-send-btn"))==null||i.addEventListener("click",()=>{ds()}),(o=c("mail-archive-btn"))==null||o.addEventListener("click",()=>{L!=null&&L.id&&us(L.id)}),(l=c("mail-toggle-unread-btn"))==null||l.addEventListener("click",()=>{L!=null&&L.id&&fs(L)})}async function dt(e){if(!S()){v("info","No city selected","Select a city to compose mail"),Ve("mail","Compose blocked without city",{replyTo:(e==null?void 0:e.id)??null});return}const t=c("compose-to");if(!t)return;const n=ie();k(t),t.append(s("option",{value:""},["Select recipient…"]));try{const a=await Xe();a.sessions.forEach(r=>{t.append(s("option",{value:r.recipient},[r.label]))}),J("mail","Compose options loaded",{city:S(),recipients:a.sessions.length,replyTo:(e==null?void 0:e.id)??null})}catch(a){oe("mail","Compose options failed",{city:S(),error:a}),P("Mail options failed",a,"Could not load recipients")}c("compose-subject").value=e?ps(e.subject??""):"",c("compose-body").value="",c("compose-reply-to").value=(e==null?void 0:e.id)??"",c("mail-compose-title").textContent=e?"Reply":"New Message",e!=null&&e.from&&(ys(t,e.from),t.value=e.from),z("compose"),yn("compose-subject"),J("mail","Compose form opened",{city:S(),replyTo:(e==null?void 0:e.id)??null,selectedRecipient:t.value||null}),n||F()}async function ds(){var l,d,p,f;const e=S();if(!e)return;const t=((l=c("compose-to"))==null?void 0:l.value)??"",n=((d=c("compose-subject"))==null?void 0:d.value.trim())??"",a=((p=c("compose-body"))==null?void 0:p.value)??"",r=((f=c("compose-reply-to"))==null?void 0:f.value)??"";if(!t||!n){v("error","Missing fields","Recipient and subject are required"),Ve("mail","Send blocked by missing fields",{bodyLength:a.length,city:e,subject:n,to:t});return}J("mail","Send requested",{bodyLength:a.length,city:e,replyTo:r||null,subject:n,to:t});const i=r?await g.POST("/v0/city/{cityName}/mail/{id}/reply",{params:{path:{cityName:e,id:r},header:T},body:{body:a,subject:n}}):await g.POST("/v0/city/{cityName}/mail",{params:{path:{cityName:e},header:T},body:{to:t,subject:n,body:a,from:"dashboard"}});if(i.error){oe("mail","Send failed",{bodyLength:a.length,city:e,error:i.error,replyTo:r||null,subject:n,to:t}),v("error","Send failed",i.error.detail??"Could not send message");return}J("mail","Send succeeded",{bodyLength:a.length,city:e,replyTo:r||null,subject:n,to:t}),v("success","Message sent",n);const o=ie();z("inbox"),L=null,o&&j(),await Ue()}async function us(e){var r;const t=S();if(!t)return;const n=await g.POST("/v0/city/{cityName}/mail/{id}/archive",{params:{path:{cityName:t,id:e},header:T}});if(n.error){v("error","Archive failed",n.error.detail??"Could not archive message");return}v("success","Archived",e);const a=((r=c("mail-detail"))==null?void 0:r.style.display)==="block";z(U),L=null,a&&j(),await Ue()}async function fs(e){const t=S();if(!t||!e.id)return;const n=e.read?"/v0/city/{cityName}/mail/{id}/mark-unread":"/v0/city/{cityName}/mail/{id}/read",a=await g.POST(n,{params:{path:{cityName:t,id:e.id},header:T}});if(a.error){v("error","Update failed",a.error.detail??"Could not update message");return}e.read=!e.read,L={...e},pn(),v("success","Updated",e.subject??e.id),await Ue()}function pn(){const e=c("mail-toggle-unread-btn");e&&(e.textContent=L!=null&&L.read?"Mark unread":"Mark read")}function ie(){var e,t;return((e=c("mail-detail"))==null?void 0:e.style.display)==="block"||((t=c("mail-compose"))==null?void 0:t.style.display)==="block"}function ps(e){return e?e.toLowerCase().startsWith("re:")?e:`Re: ${e}`:"Re:"}function ys(e,t){!t||[...e.options].some(n=>n.value===t)||e.append(s("option",{value:t},[t]))}function yn(e){var t,n;(n=(t=c("mail-panel"))==null?void 0:t.scrollIntoView)==null||n.call(t,{behavior:"smooth",block:"center"}),window.setTimeout(()=>{var a;(a=c(e))==null||a.focus()},0)}function ms(e){const t=new Map;e.forEach(i=>{i.id&&t.set(i.id,i)});function n(i){let o=i;const l=new Set;for(;o.reply_to&&o.id&&!l.has(o.id);){l.add(o.id);const d=t.get(o.reply_to);if(!d)break;o=d}return o.thread_id??o.id??Math.random().toString(36)}const a=new Map;e.forEach(i=>{const o=n(i),l=a.get(o)??{id:o,messages:[],subject:i.subject??"",unreadCount:0};l.messages.push(i),i.read||(l.unreadCount+=1),!l.subject&&i.subject&&(l.subject=i.subject),a.set(o,l)});const r=[...a.values()];return r.forEach(i=>{i.messages.sort((o,l)=>(o.created_at??"").localeCompare(l.created_at??""))}),r.sort((i,o)=>{var p,f;const l=((p=i.messages[i.messages.length-1])==null?void 0:p.created_at)??"";return(((f=o.messages[o.messages.length-1])==null?void 0:f.created_at)??"").localeCompare(l)}),r}let be="";async function St(){var o;const e=S(),t=c("convoy-list");if(!t)return;if(!e){gs();return}const n=await g.GET("/v0/city/{cityName}/convoys",{params:{path:{cityName:e},query:{limit:200}}});if(n.error||!((o=n.data)!=null&&o.items)){k(t),t.append(s("div",{class:"panel-error"},["Could not load convoys."]));return}const r=(await Promise.all(n.data.items.map(async l=>bs(e,l.id??"")))).filter(l=>l!==null);if(c("convoy-count").textContent=String(r.length),k(t),r.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No active convoys"])]));return}const i=s("tbody");r.forEach(l=>{const d=s("tr",{class:"convoy-row","data-convoy-id":l.id},[s("td",{},[s("span",{class:`badge ${ce(mn(l))}`},[hs(l)])]),s("td",{},[s("span",{class:"convoy-id"},[l.id]),l.title?s("div",{class:"convoy-title"},[l.title]):null,l.assignees.length?s("div",{class:"convoy-assignees"},l.assignees.map(p=>s("span",{class:"assignee-chip"},[p]))):null]),s("td",{class:"convoy-progress-cell"},[s("div",{class:"convoy-progress-header"},[s("span",{class:"convoy-progress-fraction"},[`${l.closed}/${l.total}`]),l.total>0?s("span",{class:"convoy-progress-pct"},[`${l.progressPct}%`]):null]),l.total>0?s("div",{class:"progress-bar"},[s("div",{class:"progress-fill",style:`width: ${l.progressPct}%;`})]):null]),s("td",{class:"convoy-work-cell"},[s("div",{class:"convoy-work-breakdown"},[l.ready>0?s("span",{class:"work-chip work-ready"},[`${l.ready} ready`]):null,l.inProgress>0?s("span",{class:"work-chip work-inprogress"},[`${l.inProgress} active`]):null,l.closed===l.total&&l.total>0?s("span",{class:"work-chip work-done"},["all done"]):null])]),s("td",{class:`activity-${l.lastActivity.colorClass}`},[s("span",{class:"activity-dot"}),` ${l.lastActivity.display}`])]);d.addEventListener("click",()=>{bn(l.id)}),i.append(d)}),t.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Status"]),s("th",{},["Convoy"]),s("th",{},["Progress"]),s("th",{},["Work"]),s("th",{},["Activity"])])]),i]))}function gs(){const e=c("convoy-list"),t=c("convoy-detail"),n=c("convoy-create-form");if(!e||!t||!n)return;const a=t.style.display==="block"||n.style.display==="block";be="",c("convoy-count").textContent="0",t.style.display="none",n.style.display="none",c("convoy-add-issue-form").style.display="none",e.style.display="block",k(e),e.append(s("div",{class:"empty-state"},[s("p",{},["Select a city to view convoys"])])),a&&j()}async function bs(e,t){var f,u,y,m;if(!t)return null;const n=await g.GET("/v0/city/{cityName}/convoy/{id}",{params:{path:{cityName:e,id:t}}});if(n.error||!n.data)return null;const a=n.data.children??[],r=new Set;let i=0,o=0,l="";a.forEach(h=>{(h.status??"").toLowerCase()!=="closed"&&(h.assignee?(o+=1,r.add(h.assignee)):i+=1),l=[l,h.created_at??""].sort().slice(-1)[0]??l});const d=((f=n.data.progress)==null?void 0:f.total)??a.length,p=((u=n.data.progress)==null?void 0:u.closed)??a.filter(h=>h.status==="closed").length;return{id:t,title:((y=n.data.convoy)==null?void 0:y.title)??t,status:(m=n.data.convoy)==null?void 0:m.status,progressPct:d>0?Math.round(p/d*100):0,total:d,closed:p,ready:i,inProgress:o,assignees:[...r].sort(),lastActivity:_e(l)}}function mn(e){return e.total>0&&e.closed===e.total?"done":e.inProgress>0?"active":e.ready>0?"waiting":e.status??"open"}function hs(e){switch(mn(e)){case"done":return"✓ Done";case"active":return"Active";case"waiting":return"Waiting";default:return e.status??"Open"}}function vs(){var e,t,n,a,r,i,o,l;(e=c("new-convoy-btn"))==null||e.addEventListener("click",()=>{gn()}),(t=c("convoy-back-btn"))==null||t.addEventListener("click",()=>ws()),(n=c("convoy-create-back-btn"))==null||n.addEventListener("click",()=>ut()),(a=c("convoy-create-cancel-btn"))==null||a.addEventListener("click",()=>ut()),(r=c("convoy-create-submit-btn"))==null||r.addEventListener("click",()=>{Ss()}),(i=c("convoy-add-issue-btn"))==null||i.addEventListener("click",()=>{c("convoy-add-issue-form").style.display="flex"}),(o=c("convoy-add-issue-cancel"))==null||o.addEventListener("click",()=>{c("convoy-add-issue-form").style.display="none"}),(l=c("convoy-add-issue-submit"))==null||l.addEventListener("click",()=>{Es()})}function gn(){var n;if(!S()){v("info","No city selected","Select a city to create a convoy");return}const e=c("convoy-create-form"),t=(e==null?void 0:e.style.display)==="block";be="",c("convoy-list").style.display="none",c("convoy-detail").style.display="none",e.style.display="block",c("convoy-create-name").value="",c("convoy-create-issues").value="",t||F(),hn("convoy-create-name"),(n=c("convoy-create-name"))==null||n.focus()}async function bn(e){var l,d,p,f,u,y,m,h;const t=S();if(!t)return;be=e,((l=c("convoy-detail"))==null?void 0:l.style.display)!=="block"&&F(),c("convoy-list").style.display="none",c("convoy-create-form").style.display="none",c("convoy-detail").style.display="block",hn("convoy-detail"),c("convoy-detail-id").textContent=e,c("convoy-detail-title").textContent=`Convoy: ${e}`,c("convoy-issues-loading").style.display="block",c("convoy-issues-table").style.display="none",c("convoy-issues-empty").style.display="none",c("convoy-add-issue-form").style.display="none";const n=await g.GET("/v0/city/{cityName}/convoy/{id}",{params:{path:{cityName:t,id:e}}});if(c("convoy-issues-loading").style.display="none",n.error||!n.data){c("convoy-issues-empty").style.display="block",c("convoy-issues-empty").querySelector("p").textContent=((d=n.error)==null?void 0:d.detail)??"Failed to load convoy";return}const a=((p=n.data.progress)==null?void 0:p.total)??((f=n.data.children)==null?void 0:f.length)??0,r=((u=n.data.progress)==null?void 0:u.closed)??((y=n.data.children)==null?void 0:y.filter(w=>w.status==="closed").length)??0;c("convoy-detail-status").className=`badge ${ce(((m=n.data.convoy)==null?void 0:m.status)??"open")}`,c("convoy-detail-status").textContent=((h=n.data.convoy)==null?void 0:h.status)??"open",c("convoy-detail-progress").textContent=`${r}/${a}`;const i=c("convoy-issues-tbody");if(!i)return;k(i);const o=n.data.children??[];if(o.length===0){c("convoy-issues-empty").style.display="block";return}o.forEach(w=>{const E=w.assignee?w.assignee:w.status==="closed"?"done":"ready";i.append(s("tr",{},[s("td",{class:"convoy-issue-status"},[s("span",{class:`badge ${ce(w.status)}`},[w.status??"unknown"])]),s("td",{},[s("span",{class:"issue-id"},[w.id??""])]),s("td",{class:"issue-title"},[w.title??w.id??""]),s("td",{},[w.assignee?s("span",{class:"badge badge-blue"},[w.assignee]):s("span",{class:"badge badge-muted"},["Unassigned"])]),s("td",{},[E])]))}),c("convoy-issues-table").style.display="table"}function ws(){const e=c("convoy-detail"),t=(e==null?void 0:e.style.display)==="block";e.style.display="none",c("convoy-list").style.display="block",t&&j()}function ut(){const e=c("convoy-create-form"),t=(e==null?void 0:e.style.display)==="block";e.style.display="none",c("convoy-list").style.display="block",t&&j()}async function Ss(){var r,i;const e=S();if(!e)return;const t=((r=c("convoy-create-name"))==null?void 0:r.value.trim())??"",n=(((i=c("convoy-create-issues"))==null?void 0:i.value)??"").split(/\s+/).map(o=>o.trim()).filter(Boolean);if(!t){v("error","Missing name","Convoy name is required");return}const a=await g.POST("/v0/city/{cityName}/convoys",{params:{path:{cityName:e},header:T},body:{title:t,items:n}});if(a.error){v("error","Create failed",a.error.detail??"Could not create convoy");return}v("success","Convoy created",t),ut(),await St()}async function Es(){const e=S();if(!e||!be)return;const t=c("convoy-add-issue-input"),n=(t==null?void 0:t.value.trim())??"";if(!n)return;const a=await g.POST("/v0/city/{cityName}/convoy/{id}/add",{params:{path:{cityName:e,id:be},header:T},body:{items:[n]}});if(a.error){v("error","Add failed",a.error.detail??"Could not add issue");return}t&&(t.value=""),c("convoy-add-issue-form").style.display="none",v("success","Issue added",n),await bn(be),await St()}function hn(e){var t,n;(n=(t=c("convoy-panel"))==null?void 0:t.scrollIntoView)==null||n.call(t,{behavior:"smooth",block:"center"}),window.setTimeout(()=>{var a;(a=c(e))==null||a.focus()},0)}const Cs=150,W=[];let Y=null,Ie="all",Be="all",Me="all";async function ks(e){W.splice(0,W.length,...wn(e)),Z()}async function Ns(){var a;const e=S(),n=(((a=(e?await g.GET("/v0/city/{cityName}/events",{params:{path:{cityName:e},query:{since:"1h",limit:100}}}):await g.GET("/v0/events",{params:{query:{since:"1h"}}})).data)==null?void 0:a.items)??[]).map(r=>Rs(r)).filter(r=>r!==null);await ks(n)}function $s(e,t){const n=S();Y==null||Y.close();const a=t?{onStatus:t}:void 0;Y=(n?i=>Ea(n,i,a):i=>Sa(i,a))(i=>{const o=En(i);e==null||e(i,o);const l=As(i);if(l){if(W.some(d=>d.id===l.id)){he("activity","Duplicate stream event ignored",{id:l.id,type:l.type});return}W.splice(0,W.length,...wn([l,...W])),Z()}})}function Ls(){Y==null||Y.close(),Y=null}function Z(){Ts();const e=c("activity-feed");if(!e)return;k(e);const t=W.filter(a=>!(Ie!=="all"&&a.category!==Ie||Be!=="all"&&a.rig!==Be||Me!=="all"&&a.actor!==Me));if(c("activity-count").textContent=String(W.length),t.length===0){e.append(s("div",{class:"empty-state"},[s("p",{},["No recent activity"])]));return}const n=s("div",{class:"tl-timeline",id:"activity-timeline"});t.forEach(a=>{n.append(s("div",{class:`tl-entry ${Ps(a.category)}`,"data-category":a.category,"data-rig":a.rig,"data-agent":a.actor??"","data-type":a.type,"data-ts":a.ts},[s("div",{class:"tl-rail"},[s("span",{class:"tl-time"},[gt(a.ts)]),s("span",{class:"tl-node"})]),s("div",{class:"tl-content"},[s("div",{class:"tl-header"},[s("span",{class:"tl-icon"},[ra(a.type)]),s("span",{class:"tl-summary"},[ia(a.type,a.actor,a.subject,a.message)])]),s("div",{class:"tl-meta"},[a.actor?s("span",{class:"tl-badge tl-badge-agent"},[B(a.actor)]):null,a.rig?s("span",{class:"tl-badge tl-badge-rig"},[a.rig]):null,s("span",{class:"tl-badge tl-badge-type"},[a.type])])])]))}),e.append(n)}function xs(){var e,t;document.addEventListener("click",n=>{var r;const a=(r=n.target)==null?void 0:r.closest(".tl-filter-btn");a&&(Ie=a.dataset.value??"all",document.querySelectorAll(".tl-filter-btn").forEach(i=>i.classList.remove("active")),a.classList.add("active"),Z())}),(e=c("tl-rig-filter"))==null||e.addEventListener("change",n=>{Be=n.currentTarget.value,Z()}),(t=c("tl-agent-filter"))==null||t.addEventListener("change",n=>{Me=n.currentTarget.value,Z()})}function Ts(){const e=c("activity-filters");if(!e||(k(e),W.length===0))return;const t=[...new Set(W.map(i=>i.rig).filter(Boolean))].sort(),n=[...new Set(W.map(i=>i.actor).filter(Boolean))].sort(),a=s("select",{class:"tl-filter-select",id:"tl-rig-filter"});a.append(s("option",{value:"all"},["All rigs"])),t.forEach(i=>a.append(s("option",{value:i,selected:i===Be},[i]))),a.addEventListener("change",()=>{Be=a.value,Z()});const r=s("select",{class:"tl-filter-select",id:"tl-agent-filter"});r.append(s("option",{value:"all"},["All agents"])),n.forEach(i=>r.append(s("option",{value:i,selected:i===Me},[B(i)]))),r.addEventListener("change",()=>{Me=r.value,Z()}),e.append(s("div",{class:"tl-filters"},[s("div",{class:"tl-filter-group"},[s("label",{},["Category:"]),Le("all","All"),Le("agent","Agent"),Le("work","Work"),Le("comms","Comms"),Le("system","System")]),s("div",{class:"tl-filter-group"},[s("label",{},["Rig:"]),a]),s("div",{class:"tl-filter-group"},[s("label",{},["Agent:"]),r])]))}function Le(e,t){const n=s("button",{class:`tl-filter-btn${Ie===e?" active":""}`,"data-filter":"category","data-value":e,type:"button"},[t]);return n.addEventListener("click",()=>{Ie=e,Z()}),n}function As(e){return e.event==="heartbeat"?null:vn(e.data,e.id)}function Rs(e){return vn(e)}function vn(e,t){if(!e.type)return null;const n=Sn(e)??S(),a=typeof e.seq=="number"?e.seq:0;return{id:_s(e,t),type:e.type,category:sa(e.type),actor:e.actor||void 0,subject:e.subject||void 0,message:e.message||void 0,ts:e.ts,scope:n,seq:a,rig:aa(e.actor)||"city"in e&&e.city||""}}function wn(e){const t=new Map;return e.forEach(n=>{t.has(n.id)||t.set(n.id,n)}),[...t.values()].sort(Os).slice(0,Cs)}function Os(e,t){const n=qs(e.ts,t.ts);if(n!==0)return n;const a=e.scope.localeCompare(t.scope);if(a!==0)return a;const r=t.seq-e.seq;if(r!==0)return r;const i=e.type.localeCompare(t.type);if(i!==0)return i;const o=(e.actor??"").localeCompare(t.actor??"");return o!==0?o:(e.subject??"").localeCompare(t.subject??"")}function qs(e,t){const n=Number.isNaN(Date.parse(e))?0:Date.parse(e);return(Number.isNaN(Date.parse(t))?0:Date.parse(t))-n}function Sn(e){if("city"in e&&typeof e.city=="string"&&e.city!=="")return e.city}function _s(e,t){const n=Sn(e)??S();if(typeof e.seq=="number"&&e.seq>0)return`${n}:${e.seq}`;const a=[e.type,e.ts,e.actor??"",e.subject??"",e.message??"",t??""].join(":");return`${n}:${a}`}function En(e){return ka(e)}function Ps(e){switch(e){case"agent":return"activity-agent";case"work":return"activity-work";case"comms":return"activity-comms";default:return"activity-system"}}async function V(){var o,l,d,p,f,u;const e=S();if(!e){js();return}const[t,n,a,r,i]=await Promise.all([g.GET("/v0/city/{cityName}/services",{params:{path:{cityName:e}}}),g.GET("/v0/city/{cityName}/rigs",{params:{path:{cityName:e},query:{git:!0}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{label:"gc:escalation",status:"open",limit:200}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{status:"in_progress",limit:500}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{label:"gc:queue",limit:200}}})]);Bs(((o=t.data)==null?void 0:o.items)??null,(l=t.error)==null?void 0:l.detail),Ms(((d=n.data)==null?void 0:d.items)??null),Us(((p=a.data)==null?void 0:p.items)??null),Ds(((f=r.data)==null?void 0:f.items)??null),zs(((u=i.data)==null?void 0:u.items)??null)}function js(){xe("services-body","services-count","Select a city to view services"),xe("rigs-body","rigs-count","Select a city to view rigs"),xe("escalations-body","escalations-count","Select a city to view escalations"),xe("assigned-body","assigned-count","Select a city to view assigned work"),xe("queues-body","queues-count","Select a city to view queues"),c("clear-assigned-btn").style.display="none"}function Is(){var e,t;(e=c("open-assign-btn"))==null||e.addEventListener("click",()=>{Cn()}),(t=c("clear-assigned-btn"))==null||t.addEventListener("click",()=>{Fs()})}function Bs(e,t){const n=c("services-body"),a=c("services-count");if(!n||!a)return;if(k(n),t){a.textContent="n/a",n.append(s("div",{class:"empty-state"},[s("p",{},[t])]));return}const r=e??[];if(a.textContent=String(r.length),r.length===0){n.append(s("div",{class:"empty-state"},[s("p",{},["No workspace services"])]));return}const i=s("tbody");r.forEach(o=>{const l=s("button",{class:"esc-btn",type:"button"},["Restart"]);l.addEventListener("click",()=>{Js(o.service_name)}),i.append(s("tr",{},[s("td",{},[s("strong",{},[o.service_name])]),s("td",{},[o.kind??"—"]),s("td",{},[s("span",{class:`badge ${ce(o.state??o.publication_state)}`},[o.state??o.publication_state??"unknown"])]),s("td",{},[o.local_state]),s("td",{},[l])]))}),n.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Name"]),s("th",{},["Kind"]),s("th",{},["Service"]),s("th",{},["Local"]),s("th",{},["Actions"])])]),i]))}function Ms(e){const t=c("rigs-body"),n=c("rigs-count");if(!t||!n)return;k(t);const a=e??[];if(n.textContent=String(a.length),a.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No rigs configured"])]));return}const r=s("tbody");a.forEach(i=>{var d;const o=s("button",{class:"esc-btn",type:"button"},[i.suspended?"Resume":"Suspend"]);o.addEventListener("click",()=>{Pt(i.name,i.suspended?"resume":"suspend")});const l=s("button",{class:"esc-btn",type:"button"},["Restart"]);l.addEventListener("click",()=>{Pt(i.name,"restart")}),r.append(s("tr",{},[s("td",{},[s("span",{class:"rig-name"},[i.name])]),s("td",{},[String(i.agent_count-i.running_count)]),s("td",{},[String(i.running_count)]),s("td",{},[(d=i.git)!=null&&d.branch?`${i.git.branch}${i.git.clean?"":"*"}`:"—"]),s("td",{},[D(i.last_activity)]),s("td",{},[o," ",l])]))}),t.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Name"]),s("th",{},["Idle"]),s("th",{},["Running"]),s("th",{},["Git"]),s("th",{},["Activity"]),s("th",{},["Actions"])])]),r]))}function Us(e){const t=c("escalations-body"),n=c("escalations-count");if(!t||!n)return;k(t);const a=(e??[]).sort((i,o)=>(i.created_at??"").localeCompare(o.created_at??""));if(n.textContent=String(a.length),a.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No escalations"])]));return}const r=s("tbody");a.forEach(i=>{const o=Ws(i.labels??[]),l=(i.labels??[]).includes("acked"),d=s("button",{class:"esc-btn esc-ack-btn",type:"button"},["👍 Ack"]);d.addEventListener("click",()=>{Vs(i)});const p=s("button",{class:"esc-btn esc-resolve-btn",type:"button"},["✓ Resolve"]);p.addEventListener("click",()=>{i.id&&Ks(i.id)});const f=s("button",{class:"esc-btn esc-reassign-btn",type:"button"},["↻ Reassign"]);f.addEventListener("click",()=>{i.id&&Qs(i.id)}),r.append(s("tr",{class:"escalation-row","data-escalation-id":i.id??""},[s("td",{},[s("span",{class:`badge ${Gs(o)}`},[o.toUpperCase()])]),s("td",{},[i.title??i.id??"",l?s("span",{class:"badge badge-cyan",style:"margin-left: 4px;"},["ACK"]):null]),s("td",{},[B(i.assignee)]),s("td",{},[D(i.created_at)]),s("td",{class:"escalation-actions"},[l?null:d,p,f])]))}),t.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Severity"]),s("th",{},["Issue"]),s("th",{},["From"]),s("th",{},["Age"]),s("th",{},["Actions"])])]),r]))}function Ds(e){const t=c("assigned-body"),n=c("assigned-count"),a=c("clear-assigned-btn");if(!t||!n||!a)return;k(t);const r=(e??[]).filter(o=>o.assignee);if(n.textContent=String(r.length),a.style.display=r.length>0?"inline-flex":"none",r.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No assigned work"])]));return}const i=s("tbody");r.forEach(o=>{const l=s("button",{class:"unassign-btn",type:"button"},["Unassign"]);l.addEventListener("click",()=>{o.id&&Hs(o.id)}),i.append(s("tr",{},[s("td",{},[s("span",{class:"assigned-id"},[o.id??""])]),s("td",{class:"assigned-title"},[Qe(o.title??"",80)]),s("td",{class:"assigned-agent"},[B(o.assignee)]),s("td",{class:"assigned-age"},[D(o.created_at)]),s("td",{},[l])]))}),t.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Bead"]),s("th",{},["Title"]),s("th",{},["Agent"]),s("th",{},["Since"]),s("th",{},[""])])]),i]))}function zs(e){const t=c("queues-body"),n=c("queues-count");if(!t||!n)return;k(t);const a=e??[];if(n.textContent=String(a.length),a.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No queues"])]));return}const r=s("tbody");a.forEach(i=>{r.append(s("tr",{},[s("td",{},[i.title??i.id??"queue"]),s("td",{},[i.id??"—"]),s("td",{},[s("span",{class:`badge ${ce(i.status)}`},[i.status??"open"])]),s("td",{},[B(i.assignee)]),s("td",{},[D(i.created_at)])]))}),t.append(s("table",{},[s("thead",{},[s("tr",{},[s("th",{},["Queue"]),s("th",{},["Bead"]),s("th",{},["Status"]),s("th",{},["Assignee"]),s("th",{},["Created"])])]),r]))}function xe(e,t,n){const a=c(e),r=c(t);!a||!r||(k(a),r.textContent="0",a.append(s("div",{class:"empty-state"},[s("p",{},[n])])))}function Ws(e){for(const t of e)if(t.startsWith("severity:"))return t.slice(9);return"medium"}function Gs(e){switch(e){case"critical":return"badge-red";case"high":return"badge-orange";case"low":return"badge-muted";default:return"badge-yellow"}}async function Cn(e=""){const t=S();if(!t)return;const n=await ht({beadID:e||void 0,beadLabel:e||void 0,mode:"assign",title:"Assign Work"});if(!n)return;const a=await g.POST("/v0/city/{cityName}/sling",{params:{path:{cityName:t},header:T},body:{bead:n.beadID,target:n.target,rig:n.rig||void 0}});if(a.error){v("error","Assign failed",a.error.detail??"Could not assign bead");return}v("success","Assigned",`${n.beadID} → ${n.target}`),await V()}async function Fs(){var r;const e=S();if(!e)return;const n=(((r=(await g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:e},query:{status:"in_progress",limit:500}}})).data)==null?void 0:r.items)??[]).filter(i=>i.assignee);if(n.length===0){v("info","Nothing to clear","No assigned work");return}await Ma({body:`Unassign ${n.length} active ${n.length===1?"bead":"beads"}?`,confirmLabel:"Unassign All",title:"Clear Assignments"})&&(await Promise.all(n.map(i=>g.POST("/v0/city/{cityName}/bead/{id}/assign",{params:{path:{cityName:e,id:i.id??""},header:T},body:{assignee:""}}))),v("success","Cleared",`${n.length} assignments removed`),await V())}async function Hs(e){const t=S();if(!t)return;const n=await g.POST("/v0/city/{cityName}/bead/{id}/assign",{params:{path:{cityName:t,id:e},header:T},body:{assignee:""}});if(n.error){v("error","Unassign failed",n.error.detail??"Could not unassign bead");return}v("success","Unassigned",e),await V()}async function Js(e){const t=S();if(!t)return;const n=await g.POST("/v0/city/{cityName}/service/{name}/restart",{params:{path:{cityName:t,name:e},header:T}});if(n.error){v("error","Service failed",n.error.detail??"Could not restart service");return}v("success","Service restarted",e),await V()}async function Pt(e,t){const n=S();if(!n)return;const a=await g.POST("/v0/city/{cityName}/rig/{name}/{action}",{params:{path:{cityName:n,name:e,action:t},header:T}});if(a.error){v("error","Rig action failed",a.error.detail??`Could not ${t} ${e}`);return}v("success","Rig updated",`${e}: ${t}`),await V()}async function Vs(e){const t=S();if(!t||!e.id)return;const n=Array.from(new Set([...e.labels??[],"acked"])),a=await g.POST("/v0/city/{cityName}/bead/{id}/update",{params:{path:{cityName:t,id:e.id},header:T},body:{labels:n}});if(a.error){v("error","Ack failed",a.error.detail??"Could not acknowledge escalation");return}v("success","Acknowledged",e.id),await V()}async function Ks(e){const t=S();if(!t)return;const n=await g.POST("/v0/city/{cityName}/bead/{id}/close",{params:{path:{cityName:t,id:e},header:T}});if(n.error){v("error","Resolve failed",n.error.detail??"Could not resolve escalation");return}v("success","Resolved",e),await V()}async function Qs(e){const t=S();if(!t)return;const n=await ht({beadID:e,beadLabel:e,mode:"reassign",title:"Reassign Escalation"});if(!n)return;const a=await g.POST("/v0/city/{cityName}/bead/{id}/assign",{params:{path:{cityName:t,id:e},header:T},body:{assignee:n.target}});if(a.error){v("error","Reassign failed",a.error.detail??"Could not reassign escalation");return}v("success","Reassigned",`${e} → ${n.target||"unassigned"}`),await V()}function Xs(e){const t=c("command-palette-overlay"),n=c("command-palette-input"),a=c("command-palette-results"),r=c("open-palette-btn");if(!t||!n||!a||!r)return;const i=t,o=n,l=a,d=r;let p=[],f=[],u=0;function y(){const b=S(),C=async(N,I)=>{const M=await I;Rt(N,JSON.stringify(M,null,2))};return[{name:"refresh",desc:"Refresh all panels",category:"Dashboard",run:()=>e.refreshAll()},{name:"supervisor health",desc:"Show supervisor health JSON",category:"Supervisor",run:()=>C("health",g.GET("/health"))},{name:"city list",desc:"Show managed cities JSON",category:"Supervisor",run:()=>C("cities",g.GET("/v0/cities"))},{name:"global events",desc:"Show recent supervisor events JSON",category:"Supervisor",run:()=>C("events",g.GET("/v0/events",{params:{query:{since:"1h"}}}))},...b?[{name:"new issue",desc:"Open the issue creation modal",category:"Work",run:()=>dn()},{name:"compose mail",desc:"Open the compose mail form",category:"Mail",run:()=>dt()},{name:"new convoy",desc:"Open the convoy creation form",category:"Convoys",run:()=>gn()},{name:"assign work",desc:"Open the assignment modal",category:"Assigned",run:()=>Cn()},{name:"status",desc:"Show current city status JSON",category:"Status",run:()=>C("status",g.GET("/v0/city/{cityName}/status",{params:{path:{cityName:b}}}))},{name:"agent list",desc:"Show current sessions JSON",category:"Status",run:()=>C("sessions",g.GET("/v0/city/{cityName}/sessions",{params:{path:{cityName:b},query:{state:"active",peek:!0}}}))},{name:"convoy list",desc:"Show current convoys JSON",category:"Convoys",run:()=>C("convoys",g.GET("/v0/city/{cityName}/convoys",{params:{path:{cityName:b},query:{limit:200}}}))},{name:"mail inbox",desc:"Show current mail JSON",category:"Mail",run:()=>C("mail",g.GET("/v0/city/{cityName}/mail",{params:{path:{cityName:b},query:{status:"all",limit:200}}}))},{name:"rig list",desc:"Show rig JSON",category:"Rigs",run:()=>C("rigs",g.GET("/v0/city/{cityName}/rigs",{params:{path:{cityName:b},query:{git:!0}}}))},{name:"list",desc:"Show open and in-progress beads JSON",category:"Beads",run:async()=>{var M,$;const[N,I]=await Promise.all([g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:b},query:{status:"open",limit:500}}}),g.GET("/v0/city/{cityName}/beads",{params:{path:{cityName:b},query:{status:"in_progress",limit:500}}})]);Rt("beads",JSON.stringify({open:((M=N.data)==null?void 0:M.items)??[],in_progress:(($=I.data)==null?void 0:$.items)??[]},null,2))}}]:[],{name:"close output",desc:"Hide the output panel",category:"Dashboard",run:()=>Zt()}].filter(N=>typeof N.run=="function")}function m(){k(l);const b=o.value.trim().toLowerCase();if(p=y(),f=p.filter(C=>b===""||C.name.includes(b)||C.desc.toLowerCase().includes(b)||C.category.toLowerCase().includes(b)),u>=f.length&&(u=0),f.length===0){l.append(s("div",{class:"command-palette-empty"},["No matching commands"]));return}f.forEach((C,N)=>{const I=s("button",{class:`command-item${N===u?" selected":""}`,type:"button"},[s("span",{class:"command-name"},[`gt ${C.name}`]),s("span",{class:"command-desc"},[C.desc]),s("span",{class:"command-category"},[C.category])]);I.addEventListener("click",()=>{E(N)}),l.append(I)})}function h(){i.classList.add("open"),o.value="",u=0,m(),o.focus()}function w(){i.classList.remove("open")}async function E(b){const C=f[b];w(),C&&(J("palette","Execute command",{category:C.category,city:S(),command:C.name}),await C.run())}d.addEventListener("click",()=>h()),i.addEventListener("click",b=>{b.target===i&&w()}),o.addEventListener("input",()=>m()),o.addEventListener("keydown",b=>{if(b.key==="ArrowDown"){u=Math.min(u+1,Math.max(f.length-1,0)),m(),b.preventDefault();return}if(b.key==="ArrowUp"){u=Math.max(u-1,0),m(),b.preventDefault();return}if(b.key==="Enter"){E(u),b.preventDefault();return}b.key==="Escape"&&w()}),document.addEventListener("keydown",b=>{(b.metaKey||b.ctrlKey)&&b.key.toLowerCase()==="k"&&(b.preventDefault(),i.classList.contains("open")?w():h())})}function Ys(){const e=c("supervisor-overview-panel"),t=c("supervisor-overview-body"),n=c("supervisor-city-count");if(!e||!t||!n)return;const a=S()==="";if(e.hidden=!a,!a)return;const r=Ht().sort((o,l)=>o.name.localeCompare(l.name));if(n.textContent=String(r.length),k(t),r.length===0){t.append(s("div",{class:"empty-state"},[s("p",{},["No managed cities available"])]));return}const i=s("tbody");r.forEach(o=>{const l=o.phasesCompleted.length>0?o.phasesCompleted.join(", "):"—",d=s("a",{class:"supervisor-city-link",href:`?city=${encodeURIComponent(o.name)}`},["Open"]);i.append(s("tr",{},[s("td",{},[s("strong",{},[o.name])]),s("td",{},[s("span",{class:`badge ${o.error?"badge-red":o.running?"badge-green":"badge-muted"}`},[o.error?"Error":o.running?"Running":"Stopped"])]),s("td",{},[o.status??"—"]),s("td",{class:"supervisor-city-phases"},[l]),s("td",{class:"supervisor-city-error"},[o.error??"—"]),s("td",{class:"supervisor-city-actions"},[d])]))}),t.append(s("table",{class:"supervisor-city-table"},[s("thead",{},[s("tr",{},[s("th",{},["City"]),s("th",{},["State"]),s("th",{},["Status"]),s("th",{},["Phases"]),s("th",{},["Error"]),s("th",{},[""])])]),i]))}const Zs=["convoy-panel","crew-panel","rigged-panel","mail-panel","escalations-panel","services-panel","rigs-panel","pooled-panel","queues-panel","beads-panel","assigned-panel","agent-log-drawer"];async function er(){bt()||await Ee()}async function tr(){bt()||await Ee().catch(e=>P("Catch-up refresh failed",e))}async function nr(){mt(),await Ee(!0)}function Et(){const e=Jt();if(e.kind==="not-running"||e.kind==="unknown"){Ls(),st("connecting");return}st("connecting"),$s(t=>{const n=En(t);!n||n==="heartbeat"||(ea(n),!bt()&&Ee().catch(a=>P("Refresh failed",a)))},st)}function st(e){const t=Ct("connection-status");if(!t)return;const n={connecting:"Connecting…",live:"Live",reconnecting:"Reconnecting…"};t.replaceChildren(document.createTextNode(n[e])),t.classList.remove("connection-live","connection-connecting","connection-reconnecting"),t.classList.add(`connection-${e}`)}function ar(){ba(),Ba(),Ra(),Ga(),ls(),vs(),xs(),Is(),Xs({refreshAll:er})}async function sr(){Vn(),J("dashboard","Boot start",{city:S(),href:window.location.href}),ar(),ir(),ga(()=>{tr()}),await nr(),Et(),J("dashboard","Boot complete",{city:S(),href:window.location.href})}function Ct(e){return document.getElementById(e)}sr().catch(e=>P("Dashboard boot failed",e));function rr(){const e=S()!=="";cr(e),De("new-convoy-btn",e,"Select a city to create a convoy"),De("new-issue-btn",e,"Select a city to create a bead"),De("compose-mail-btn",e,"Select a city to compose mail"),De("open-assign-btn",e,"Select a city to assign work")}function De(e,t,n){const a=Ct(e);a&&(a.dataset.defaultTitle===void 0&&(a.dataset.defaultTitle=a.title||""),a.disabled=!t,a.title=t?a.dataset.defaultTitle:n)}function ir(){document.addEventListener("click",e=>{var a;const t=(a=e.target)==null?void 0:a.closest("a.city-tab");if(!t)return;const n=t.href;!n||n===window.location.href||(e.preventDefault(),or(n))}),window.addEventListener("popstate",()=>{J("dashboard","Popstate navigation",{href:window.location.href}),on(),yt(),mt(),Ee().catch(e=>P("Refresh failed",e)),Et()})}async function or(e){J("dashboard","Navigate city scope",{nextURL:e}),on(),window.history.pushState({},"",e),yt(),mt(),await Ee(),Et()}function cr(e){Zs.forEach(t=>{const n=Ct(t);if(!n)return;const a=!e&&n.classList.contains("expanded");if(n.hidden=!e,a){n.classList.remove("expanded");const r=n.querySelector(".expand-btn");r&&(r.textContent="Expand"),j()}})}async function Ee(e=!1){yt(),rr();const t=Yn(e);if(t.size===0)return;t.has("options")&&Ia(),t.has("cities")&&await ta().catch(l=>P("City tabs failed",l));const n=[],r=Jt().kind==="running";ae(n,t,"status",()=>oa()),ae(n,t,"activity",()=>Ns()),r&&(ae(n,t,"crew",()=>Na()),ae(n,t,"issues",()=>de()),ae(n,t,"mail",()=>Ue()),ae(n,t,"convoys",()=>St()),ae(n,t,"admin",()=>V()));const o=(await Promise.allSettled(n)).find(l=>l.status==="rejected");o&&P("Panel refresh failed",o.reason),(t.has("supervisor")||t.has("cities"))&&Ys()}function ae(e,t,n,a){t.has(n)&&e.push(a())} diff --git a/cmd/gc/dashboard/web/openapi-ts.config.ts b/cmd/gc/dashboard/web/openapi-ts.config.ts index e191f2f22a..1ccb2ab929 100644 --- a/cmd/gc/dashboard/web/openapi-ts.config.ts +++ b/cmd/gc/dashboard/web/openapi-ts.config.ts @@ -6,7 +6,7 @@ import { defineConfig } from "@hey-api/openapi-ts"; // path construction, request/response typing, event discrimination, // retry, and auth headers all flow through generated code. // -// See specs/architecture.md §6 "Tooling landscape" for the rationale. +// See engdocs/architecture/api-control-plane.md §6 "Tooling landscape" for the rationale. export default defineConfig({ input: "../../../../internal/api/openapi.json", output: { diff --git a/cmd/gc/dashboard/web/src/api.ts b/cmd/gc/dashboard/web/src/api.ts index 269f4fcf85..87d6eb57c0 100644 --- a/cmd/gc/dashboard/web/src/api.ts +++ b/cmd/gc/dashboard/web/src/api.ts @@ -58,11 +58,11 @@ export type CityInfoRecord = DashboardSchema["CityInfo"]; // The supervisor's CSRF middleware requires `X-GC-Request: true` on // every mutation. Attaching it as a default request editor means // callers never have to remember the header. +export const mutationHeaders = { "X-GC-Request": "true" } as const; + export const api = createClient({ baseUrl: supervisorBaseURL(), - headers: { - "X-GC-Request": "true", - }, + headers: mutationHeaders, }); // Configure the hey-api SSE client with the same base URL + CSRF @@ -71,9 +71,7 @@ export const api = createClient({ // client instance. sseClient.setConfig({ baseUrl: supervisorBaseURL(), - headers: { - "X-GC-Request": "true", - }, + headers: mutationHeaders, }); api.use({ @@ -134,13 +132,15 @@ export function cityAPI(cityName: string) { beadAssign(id: string, assignee: string) { return api.POST("/v0/city/{cityName}/bead/{id}/assign", { - params: { path: { cityName, id } }, + params: { path: { cityName, id }, header: mutationHeaders }, body: { assignee }, }); }, beadClose(id: string) { - return api.POST("/v0/city/{cityName}/bead/{id}/close", { params: { path: { cityName, id } } }); + return api.POST("/v0/city/{cityName}/bead/{id}/close", { + params: { path: { cityName, id }, header: mutationHeaders }, + }); }, beadDeps(id: string) { @@ -148,12 +148,14 @@ export function cityAPI(cityName: string) { }, beadReopen(id: string) { - return api.POST("/v0/city/{cityName}/bead/{id}/reopen", { params: { path: { cityName, id } } }); + return api.POST("/v0/city/{cityName}/bead/{id}/reopen", { + params: { path: { cityName, id }, header: mutationHeaders }, + }); }, beadUpdate(id: string, body: { labels?: string[]; priority?: number }) { return api.POST("/v0/city/{cityName}/bead/{id}/update", { - params: { path: { cityName, id } }, + params: { path: { cityName, id }, header: mutationHeaders }, body, }); }, @@ -172,7 +174,7 @@ export function cityAPI(cityName: string) { title: string; }) { return api.POST("/v0/city/{cityName}/beads", { - params: { path: { cityName } }, + params: { path: { cityName }, header: mutationHeaders }, body, }); }, @@ -183,7 +185,7 @@ export function cityAPI(cityName: string) { convoyAdd(id: string, items: string[]) { return api.POST("/v0/city/{cityName}/convoy/{id}/add", { - params: { path: { cityName, id } }, + params: { path: { cityName, id }, header: mutationHeaders }, body: { items }, }); }, @@ -196,7 +198,7 @@ export function cityAPI(cityName: string) { createConvoy(title: string, items: string[]) { return api.POST("/v0/city/{cityName}/convoys", { - params: { path: { cityName } }, + params: { path: { cityName }, header: mutationHeaders }, body: { title, items }, }); }, @@ -224,7 +226,7 @@ export function cityAPI(cityName: string) { rigAction(name: string, action: string) { return api.POST("/v0/city/{cityName}/rig/{name}/{action}", { - params: { path: { cityName, name, action } }, + params: { path: { cityName, name, action }, header: mutationHeaders }, }); }, @@ -234,7 +236,7 @@ export function cityAPI(cityName: string) { serviceRestart(name: string) { return api.POST("/v0/city/{cityName}/service/{name}/restart", { - params: { path: { cityName, name } }, + params: { path: { cityName, name }, header: mutationHeaders }, }); }, @@ -252,7 +254,7 @@ export function cityAPI(cityName: string) { sling(body: { bead: string; rig?: string; target: string }) { return api.POST("/v0/city/{cityName}/sling", { - params: { path: { cityName } }, + params: { path: { cityName }, header: mutationHeaders }, body, }); }, diff --git a/cmd/gc/dashboard/web/src/generated/client.gen.ts b/cmd/gc/dashboard/web/src/generated/client.gen.ts new file mode 100644 index 0000000000..cab3c70195 --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/client.gen.ts @@ -0,0 +1,16 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { type ClientOptions, type Config, createClient, createConfig } from './client'; +import type { ClientOptions as ClientOptions2 } from './types.gen'; + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = (override?: Config) => Config & T>; + +export const client = createClient(createConfig()); diff --git a/cmd/gc/dashboard/web/src/generated/client/client.gen.ts b/cmd/gc/dashboard/web/src/generated/client/client.gen.ts new file mode 100644 index 0000000000..9ec9ad887c --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/client/client.gen.ts @@ -0,0 +1,298 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { createSseClient } from '../core/serverSentEvents.gen'; +import type { HttpMethod } from '../core/types.gen'; +import { getValidRequestBody } from '../core/utils.gen'; +import type { Client, Config, RequestOptions, ResolvedRequestOptions } from './types.gen'; +import { + buildUrl, + createConfig, + createInterceptors, + getParseAs, + mergeConfigs, + mergeHeaders, + setAuthParams, +} from './utils.gen'; + +type ReqInit = Omit & { + body?: any; + headers: ReturnType; +}; + +export const createClient = (config: Config = {}): Client => { + let _config = mergeConfigs(createConfig(), config); + + const getConfig = (): Config => ({ ..._config }); + + const setConfig = (config: Config): Config => { + _config = mergeConfigs(_config, config); + return getConfig(); + }; + + const interceptors = createInterceptors(); + + const beforeRequest = async < + TData = unknown, + TResponseStyle extends 'data' | 'fields' = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, + >( + options: RequestOptions, + ) => { + const opts = { + ..._config, + ...options, + fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, + headers: mergeHeaders(_config.headers, options.headers), + serializedBody: undefined as string | undefined, + }; + + if (opts.security) { + await setAuthParams({ + ...opts, + security: opts.security, + }); + } + + if (opts.requestValidator) { + await opts.requestValidator(opts); + } + + if (opts.body !== undefined && opts.bodySerializer) { + opts.serializedBody = opts.bodySerializer(opts.body) as string | undefined; + } + + // remove Content-Type header if body is empty to avoid sending invalid requests + if (opts.body === undefined || opts.serializedBody === '') { + opts.headers.delete('Content-Type'); + } + + const resolvedOpts = opts as typeof opts & + ResolvedRequestOptions; + const url = buildUrl(resolvedOpts); + + return { opts: resolvedOpts, url }; + }; + + const request: Client['request'] = async (options) => { + const { opts, url } = await beforeRequest(options); + const requestInit: ReqInit = { + redirect: 'follow', + ...opts, + body: getValidRequestBody(opts), + }; + + let request = new Request(url, requestInit); + + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } + } + + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = opts.fetch!; + let response: Response; + + try { + response = await _fetch(request); + } catch (error) { + // Handle fetch exceptions (AbortError, network errors, etc.) + let finalError = error; + + for (const fn of interceptors.error.fns) { + if (fn) { + finalError = (await fn(error, undefined as any, request, opts)) as unknown; + } + } + + finalError = finalError || ({} as unknown); + + if (opts.throwOnError) { + throw finalError; + } + + // Return error response + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + request, + response: undefined as any, + }; + } + + for (const fn of interceptors.response.fns) { + if (fn) { + response = await fn(response, request, opts); + } + } + + const result = { + request, + response, + }; + + if (response.ok) { + const parseAs = + (opts.parseAs === 'auto' + ? getParseAs(response.headers.get('Content-Type')) + : opts.parseAs) ?? 'json'; + + if (response.status === 204 || response.headers.get('Content-Length') === '0') { + let emptyData: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'text': + emptyData = await response[parseAs](); + break; + case 'formData': + emptyData = new FormData(); + break; + case 'stream': + emptyData = response.body; + break; + case 'json': + default: + emptyData = {}; + break; + } + return opts.responseStyle === 'data' + ? emptyData + : { + data: emptyData, + ...result, + }; + } + + let data: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'formData': + case 'text': + data = await response[parseAs](); + break; + case 'json': { + // Some servers return 200 with no Content-Length and empty body. + // response.json() would throw; read as text and parse if non-empty. + const text = await response.text(); + data = text ? JSON.parse(text) : {}; + break; + } + case 'stream': + return opts.responseStyle === 'data' + ? response.body + : { + data: response.body, + ...result, + }; + } + + if (parseAs === 'json') { + if (opts.responseValidator) { + await opts.responseValidator(data); + } + + if (opts.responseTransformer) { + data = await opts.responseTransformer(data); + } + } + + return opts.responseStyle === 'data' + ? data + : { + data, + ...result, + }; + } + + const textError = await response.text(); + let jsonError: unknown; + + try { + jsonError = JSON.parse(textError); + } catch { + // noop + } + + const error = jsonError ?? textError; + let finalError = error; + + for (const fn of interceptors.error.fns) { + if (fn) { + finalError = (await fn(error, response, request, opts)) as string; + } + } + + finalError = finalError || ({} as string); + + if (opts.throwOnError) { + throw finalError; + } + + // TODO: we probably want to return error and improve types + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + ...result, + }; + }; + + const makeMethodFn = (method: Uppercase) => (options: RequestOptions) => + request({ ...options, method }); + + const makeSseFn = (method: Uppercase) => async (options: RequestOptions) => { + const { opts, url } = await beforeRequest(options); + return createSseClient({ + ...opts, + body: opts.body as BodyInit | null | undefined, + headers: opts.headers as unknown as Record, + method, + onRequest: async (url, init) => { + let request = new Request(url, init); + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } + } + return request; + }, + serializedBody: getValidRequestBody(opts) as BodyInit | null | undefined, + url, + }); + }; + + const _buildUrl: Client['buildUrl'] = (options) => buildUrl({ ..._config, ...options }); + + return { + buildUrl: _buildUrl, + connect: makeMethodFn('CONNECT'), + delete: makeMethodFn('DELETE'), + get: makeMethodFn('GET'), + getConfig, + head: makeMethodFn('HEAD'), + interceptors, + options: makeMethodFn('OPTIONS'), + patch: makeMethodFn('PATCH'), + post: makeMethodFn('POST'), + put: makeMethodFn('PUT'), + request, + setConfig, + sse: { + connect: makeSseFn('CONNECT'), + delete: makeSseFn('DELETE'), + get: makeSseFn('GET'), + head: makeSseFn('HEAD'), + options: makeSseFn('OPTIONS'), + patch: makeSseFn('PATCH'), + post: makeSseFn('POST'), + put: makeSseFn('PUT'), + trace: makeSseFn('TRACE'), + }, + trace: makeMethodFn('TRACE'), + } as Client; +}; diff --git a/cmd/gc/dashboard/web/src/generated/client/index.ts b/cmd/gc/dashboard/web/src/generated/client/index.ts new file mode 100644 index 0000000000..b295edeca0 --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/client/index.ts @@ -0,0 +1,25 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { Auth } from '../core/auth.gen'; +export type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +export { + formDataBodySerializer, + jsonBodySerializer, + urlSearchParamsBodySerializer, +} from '../core/bodySerializer.gen'; +export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; +export { createClient } from './client.gen'; +export type { + Client, + ClientOptions, + Config, + CreateClientConfig, + Options, + RequestOptions, + RequestResult, + ResolvedRequestOptions, + ResponseStyle, + TDataShape, +} from './types.gen'; +export { createConfig, mergeHeaders } from './utils.gen'; diff --git a/cmd/gc/dashboard/web/src/generated/client/types.gen.ts b/cmd/gc/dashboard/web/src/generated/client/types.gen.ts new file mode 100644 index 0000000000..9813eeaba6 --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/client/types.gen.ts @@ -0,0 +1,214 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth } from '../core/auth.gen'; +import type { + ServerSentEventsOptions, + ServerSentEventsResult, +} from '../core/serverSentEvents.gen'; +import type { Client as CoreClient, Config as CoreConfig } from '../core/types.gen'; +import type { Middleware } from './utils.gen'; + +export type ResponseStyle = 'data' | 'fields'; + +export interface Config + extends Omit, CoreConfig { + /** + * Base URL for all requests made by this client. + */ + baseUrl?: T['baseUrl']; + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch; + /** + * Please don't use the Fetch client for Next.js applications. The `next` + * options won't have any effect. + * + * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. + */ + next?: never; + /** + * Return the response data parsed in a specified format. By default, `auto` + * will infer the appropriate method from the `Content-Type` response header. + * You can override this behavior with any of the {@link Body} methods. + * Select `stream` if you don't want to parse response data at all. + * + * @default 'auto' + */ + parseAs?: 'arrayBuffer' | 'auto' | 'blob' | 'formData' | 'json' | 'stream' | 'text'; + /** + * Should we return only data or multiple fields (data, error, response, etc.)? + * + * @default 'fields' + */ + responseStyle?: ResponseStyle; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: T['throwOnError']; +} + +export interface RequestOptions< + TData = unknown, + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> + extends + Config<{ + responseStyle: TResponseStyle; + throwOnError: ThrowOnError; + }>, + Pick< + ServerSentEventsOptions, + | 'onRequest' + | 'onSseError' + | 'onSseEvent' + | 'sseDefaultRetryDelay' + | 'sseMaxRetryAttempts' + | 'sseMaxRetryDelay' + > { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: unknown; + path?: Record; + query?: Record; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} + +export interface ResolvedRequestOptions< + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends RequestOptions { + serializedBody?: string; +} + +export type RequestResult< + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = ThrowOnError extends true + ? Promise< + TResponseStyle extends 'data' + ? TData extends Record + ? TData[keyof TData] + : TData + : { + data: TData extends Record ? TData[keyof TData] : TData; + request: Request; + response: Response; + } + > + : Promise< + TResponseStyle extends 'data' + ? (TData extends Record ? TData[keyof TData] : TData) | undefined + : ( + | { + data: TData extends Record ? TData[keyof TData] : TData; + error: undefined; + } + | { + data: undefined; + error: TError extends Record ? TError[keyof TError] : TError; + } + ) & { + request: Request; + response: Response; + } + >; + +export interface ClientOptions { + baseUrl?: string; + responseStyle?: ResponseStyle; + throwOnError?: boolean; +} + +type MethodFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => RequestResult; + +type SseFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => Promise>; + +type RequestFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'> & + Pick>, 'method'>, +) => RequestResult; + +type BuildUrlFn = < + TData extends { + body?: unknown; + path?: Record; + query?: Record; + url: string; + }, +>( + options: TData & Options, +) => string; + +export type Client = CoreClient & { + interceptors: Middleware; +}; + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = ( + override?: Config, +) => Config & T>; + +export interface TDataShape { + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; +} + +type OmitKeys = Pick>; + +export type Options< + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, + TResponse = unknown, + TResponseStyle extends ResponseStyle = 'fields', +> = OmitKeys< + RequestOptions, + 'body' | 'path' | 'query' | 'url' +> & + ([TData] extends [never] ? unknown : Omit); diff --git a/cmd/gc/dashboard/web/src/generated/client/utils.gen.ts b/cmd/gc/dashboard/web/src/generated/client/utils.gen.ts new file mode 100644 index 0000000000..5162192d8a --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/client/utils.gen.ts @@ -0,0 +1,316 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { getAuthToken } from '../core/auth.gen'; +import type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +import { jsonBodySerializer } from '../core/bodySerializer.gen'; +import { + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from '../core/pathSerializer.gen'; +import { getUrl } from '../core/utils.gen'; +import type { Client, ClientOptions, Config, RequestOptions } from './types.gen'; + +export const createQuerySerializer = ({ + parameters = {}, + ...args +}: QuerySerializerOptions = {}) => { + const querySerializer = (queryParams: T) => { + const search: string[] = []; + if (queryParams && typeof queryParams === 'object') { + for (const name in queryParams) { + const value = queryParams[name]; + + if (value === undefined || value === null) { + continue; + } + + const options = parameters[name] || args; + + if (Array.isArray(value)) { + const serializedArray = serializeArrayParam({ + allowReserved: options.allowReserved, + explode: true, + name, + style: 'form', + value, + ...options.array, + }); + if (serializedArray) search.push(serializedArray); + } else if (typeof value === 'object') { + const serializedObject = serializeObjectParam({ + allowReserved: options.allowReserved, + explode: true, + name, + style: 'deepObject', + value: value as Record, + ...options.object, + }); + if (serializedObject) search.push(serializedObject); + } else { + const serializedPrimitive = serializePrimitiveParam({ + allowReserved: options.allowReserved, + name, + value: value as string, + }); + if (serializedPrimitive) search.push(serializedPrimitive); + } + } + } + return search.join('&'); + }; + return querySerializer; +}; + +/** + * Infers parseAs value from provided Content-Type header. + */ +export const getParseAs = (contentType: string | null): Exclude => { + if (!contentType) { + // If no Content-Type header is provided, the best we can do is return the raw response body, + // which is effectively the same as the 'stream' option. + return 'stream'; + } + + const cleanContent = contentType.split(';')[0]?.trim(); + + if (!cleanContent) { + return; + } + + if (cleanContent.startsWith('application/json') || cleanContent.endsWith('+json')) { + return 'json'; + } + + if (cleanContent === 'multipart/form-data') { + return 'formData'; + } + + if ( + ['application/', 'audio/', 'image/', 'video/'].some((type) => cleanContent.startsWith(type)) + ) { + return 'blob'; + } + + if (cleanContent.startsWith('text/')) { + return 'text'; + } + + return; +}; + +const checkForExistence = ( + options: Pick & { + headers: Headers; + }, + name?: string, +): boolean => { + if (!name) { + return false; + } + if ( + options.headers.has(name) || + options.query?.[name] || + options.headers.get('Cookie')?.includes(`${name}=`) + ) { + return true; + } + return false; +}; + +export const setAuthParams = async ({ + security, + ...options +}: Pick, 'security'> & + Pick & { + headers: Headers; + }) => { + for (const auth of security) { + if (checkForExistence(options, auth.name)) { + continue; + } + + const token = await getAuthToken(auth, options.auth); + + if (!token) { + continue; + } + + const name = auth.name ?? 'Authorization'; + + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'cookie': + options.headers.append('Cookie', `${name}=${token}`); + break; + case 'header': + default: + options.headers.set(name, token); + break; + } + } +}; + +export const buildUrl: Client['buildUrl'] = (options) => + getUrl({ + baseUrl: options.baseUrl as string, + path: options.path, + query: options.query, + querySerializer: + typeof options.querySerializer === 'function' + ? options.querySerializer + : createQuerySerializer(options.querySerializer), + url: options.url, + }); + +export const mergeConfigs = (a: Config, b: Config): Config => { + const config = { ...a, ...b }; + if (config.baseUrl?.endsWith('/')) { + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); + } + config.headers = mergeHeaders(a.headers, b.headers); + return config; +}; + +const headersEntries = (headers: Headers): Array<[string, string]> => { + const entries: Array<[string, string]> = []; + headers.forEach((value, key) => { + entries.push([key, value]); + }); + return entries; +}; + +export const mergeHeaders = ( + ...headers: Array['headers'] | undefined> +): Headers => { + const mergedHeaders = new Headers(); + for (const header of headers) { + if (!header) { + continue; + } + + const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header); + + for (const [key, value] of iterator) { + if (value === null) { + mergedHeaders.delete(key); + } else if (Array.isArray(value)) { + for (const v of value) { + mergedHeaders.append(key, v as string); + } + } else if (value !== undefined) { + // assume object headers are meant to be JSON stringified, i.e., their + // content value in OpenAPI specification is 'application/json' + mergedHeaders.set( + key, + typeof value === 'object' ? JSON.stringify(value) : (value as string), + ); + } + } + } + return mergedHeaders; +}; + +type ErrInterceptor = ( + error: Err, + response: Res, + request: Req, + options: Options, +) => Err | Promise; + +type ReqInterceptor = (request: Req, options: Options) => Req | Promise; + +type ResInterceptor = ( + response: Res, + request: Req, + options: Options, +) => Res | Promise; + +class Interceptors { + fns: Array = []; + + clear(): void { + this.fns = []; + } + + eject(id: number | Interceptor): void { + const index = this.getInterceptorIndex(id); + if (this.fns[index]) { + this.fns[index] = null; + } + } + + exists(id: number | Interceptor): boolean { + const index = this.getInterceptorIndex(id); + return Boolean(this.fns[index]); + } + + getInterceptorIndex(id: number | Interceptor): number { + if (typeof id === 'number') { + return this.fns[id] ? id : -1; + } + return this.fns.indexOf(id); + } + + update(id: number | Interceptor, fn: Interceptor): number | Interceptor | false { + const index = this.getInterceptorIndex(id); + if (this.fns[index]) { + this.fns[index] = fn; + return id; + } + return false; + } + + use(fn: Interceptor): number { + this.fns.push(fn); + return this.fns.length - 1; + } +} + +export interface Middleware { + error: Interceptors>; + request: Interceptors>; + response: Interceptors>; +} + +export const createInterceptors = (): Middleware< + Req, + Res, + Err, + Options +> => ({ + error: new Interceptors>(), + request: new Interceptors>(), + response: new Interceptors>(), +}); + +const defaultQuerySerializer = createQuerySerializer({ + allowReserved: false, + array: { + explode: true, + style: 'form', + }, + object: { + explode: true, + style: 'deepObject', + }, +}); + +const defaultHeaders = { + 'Content-Type': 'application/json', +}; + +export const createConfig = ( + override: Config & T> = {}, +): Config & T> => ({ + ...jsonBodySerializer, + headers: defaultHeaders, + parseAs: 'auto', + querySerializer: defaultQuerySerializer, + ...override, +}); diff --git a/cmd/gc/dashboard/web/src/generated/core/auth.gen.ts b/cmd/gc/dashboard/web/src/generated/core/auth.gen.ts new file mode 100644 index 0000000000..3ebf994788 --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/core/auth.gen.ts @@ -0,0 +1,41 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type AuthToken = string | undefined; + +export interface Auth { + /** + * Which part of the request do we use to send the auth? + * + * @default 'header' + */ + in?: 'header' | 'query' | 'cookie'; + /** + * Header or query parameter name. + * + * @default 'Authorization' + */ + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +export const getAuthToken = async ( + auth: Auth, + callback: ((auth: Auth) => Promise | AuthToken) | AuthToken, +): Promise => { + const token = typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; + } + + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; + } + + return token; +}; diff --git a/cmd/gc/dashboard/web/src/generated/core/bodySerializer.gen.ts b/cmd/gc/dashboard/web/src/generated/core/bodySerializer.gen.ts new file mode 100644 index 0000000000..67daca60f8 --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/core/bodySerializer.gen.ts @@ -0,0 +1,82 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { ArrayStyle, ObjectStyle, SerializerOptions } from './pathSerializer.gen'; + +export type QuerySerializer = (query: Record) => string; + +export type BodySerializer = (body: unknown) => unknown; + +type QuerySerializerOptionsObject = { + allowReserved?: boolean; + array?: Partial>; + object?: Partial>; +}; + +export type QuerySerializerOptions = QuerySerializerOptionsObject & { + /** + * Per-parameter serialization overrides. When provided, these settings + * override the global array/object settings for specific parameter names. + */ + parameters?: Record; +}; + +const serializeFormDataPair = (data: FormData, key: string, value: unknown): void => { + if (typeof value === 'string' || value instanceof Blob) { + data.append(key, value); + } else if (value instanceof Date) { + data.append(key, value.toISOString()); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +const serializeUrlSearchParamsPair = (data: URLSearchParams, key: string, value: unknown): void => { + if (typeof value === 'string') { + data.append(key, value); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +export const formDataBodySerializer = { + bodySerializer: (body: unknown): FormData => { + const data = new FormData(); + + Object.entries(body as Record).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeFormDataPair(data, key, v)); + } else { + serializeFormDataPair(data, key, value); + } + }); + + return data; + }, +}; + +export const jsonBodySerializer = { + bodySerializer: (body: unknown): string => + JSON.stringify(body, (_key, value) => (typeof value === 'bigint' ? value.toString() : value)), +}; + +export const urlSearchParamsBodySerializer = { + bodySerializer: (body: unknown): string => { + const data = new URLSearchParams(); + + Object.entries(body as Record).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); + } else { + serializeUrlSearchParamsPair(data, key, value); + } + }); + + return data.toString(); + }, +}; diff --git a/cmd/gc/dashboard/web/src/generated/core/params.gen.ts b/cmd/gc/dashboard/web/src/generated/core/params.gen.ts new file mode 100644 index 0000000000..7955601a5c --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/core/params.gen.ts @@ -0,0 +1,169 @@ +// This file is auto-generated by @hey-api/openapi-ts + +type Slot = 'body' | 'headers' | 'path' | 'query'; + +export type Field = + | { + in: Exclude; + /** + * Field name. This is the name we want the user to see and use. + */ + key: string; + /** + * Field mapped name. This is the name we want to use in the request. + * If omitted, we use the same value as `key`. + */ + map?: string; + } + | { + in: Extract; + /** + * Key isn't required for bodies. + */ + key?: string; + map?: string; + } + | { + /** + * Field name. This is the name we want the user to see and use. + */ + key: string; + /** + * Field mapped name. This is the name we want to use in the request. + * If `in` is omitted, `map` aliases `key` to the transport layer. + */ + map: Slot; + }; + +export interface Fields { + allowExtra?: Partial>; + args?: ReadonlyArray; +} + +export type FieldsConfig = ReadonlyArray; + +const extraPrefixesMap: Record = { + $body_: 'body', + $headers_: 'headers', + $path_: 'path', + $query_: 'query', +}; +const extraPrefixes = Object.entries(extraPrefixesMap); + +type KeyMap = Map< + string, + | { + in: Slot; + map?: string; + } + | { + in?: never; + map: Slot; + } +>; + +const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { + if (!map) { + map = new Map(); + } + + for (const config of fields) { + if ('in' in config) { + if (config.key) { + map.set(config.key, { + in: config.in, + map: config.map, + }); + } + } else if ('key' in config) { + map.set(config.key, { + map: config.map, + }); + } else if (config.args) { + buildKeyMap(config.args, map); + } + } + + return map; +}; + +interface Params { + body: unknown; + headers: Record; + path: Record; + query: Record; +} + +const stripEmptySlots = (params: Params) => { + for (const [slot, value] of Object.entries(params)) { + if (value && typeof value === 'object' && !Array.isArray(value) && !Object.keys(value).length) { + delete params[slot as Slot]; + } + } +}; + +export const buildClientParams = (args: ReadonlyArray, fields: FieldsConfig) => { + const params: Params = { + body: {}, + headers: {}, + path: {}, + query: {}, + }; + + const map = buildKeyMap(fields); + + let config: FieldsConfig[number] | undefined; + + for (const [index, arg] of args.entries()) { + if (fields[index]) { + config = fields[index]; + } + + if (!config) { + continue; + } + + if ('in' in config) { + if (config.key) { + const field = map.get(config.key)!; + const name = field.map || config.key; + if (field.in) { + (params[field.in] as Record)[name] = arg; + } + } else { + params.body = arg; + } + } else { + for (const [key, value] of Object.entries(arg ?? {})) { + const field = map.get(key); + + if (field) { + if (field.in) { + const name = field.map || key; + (params[field.in] as Record)[name] = value; + } else { + params[field.map] = value; + } + } else { + const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix)); + + if (extra) { + const [prefix, slot] = extra; + (params[slot] as Record)[key.slice(prefix.length)] = value; + } else if ('allowExtra' in config && config.allowExtra) { + for (const [slot, allowed] of Object.entries(config.allowExtra)) { + if (allowed) { + (params[slot as Slot] as Record)[key] = value; + break; + } + } + } + } + } + } + } + + stripEmptySlots(params); + + return params; +}; diff --git a/cmd/gc/dashboard/web/src/generated/core/pathSerializer.gen.ts b/cmd/gc/dashboard/web/src/generated/core/pathSerializer.gen.ts new file mode 100644 index 0000000000..994b2848c6 --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/core/pathSerializer.gen.ts @@ -0,0 +1,171 @@ +// This file is auto-generated by @hey-api/openapi-ts + +interface SerializeOptions extends SerializePrimitiveOptions, SerializerOptions {} + +interface SerializePrimitiveOptions { + allowReserved?: boolean; + name: string; +} + +export interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} + +export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; +type MatrixStyle = 'label' | 'matrix' | 'simple'; +export type ObjectStyle = 'form' | 'deepObject'; +type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; + +interface SerializePrimitiveParam extends SerializePrimitiveOptions { + value: string; +} + +export const separatorArrayExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'form': + return ','; + case 'pipeDelimited': + return '|'; + case 'spaceDelimited': + return '%20'; + default: + return ','; + } +}; + +export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const serializeArrayParam = ({ + allowReserved, + explode, + name, + style, + value, +}: SerializeOptions & { + value: unknown[]; +}) => { + if (!explode) { + const joinedValues = ( + allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) + ).join(separatorArrayNoExplode(style)); + switch (style) { + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + case 'simple': + return joinedValues; + default: + return `${name}=${joinedValues}`; + } + } + + const separator = separatorArrayExplode(style); + const joinedValues = value + .map((v) => { + if (style === 'label' || style === 'simple') { + return allowReserved ? v : encodeURIComponent(v as string); + } + + return serializePrimitiveParam({ + allowReserved, + name, + value: v as string, + }); + }) + .join(separator); + return style === 'label' || style === 'matrix' ? separator + joinedValues : joinedValues; +}; + +export const serializePrimitiveParam = ({ + allowReserved, + name, + value, +}: SerializePrimitiveParam) => { + if (value === undefined || value === null) { + return ''; + } + + if (typeof value === 'object') { + throw new Error( + 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', + ); + } + + return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; +}; + +export const serializeObjectParam = ({ + allowReserved, + explode, + name, + style, + value, + valueOnly, +}: SerializeOptions & { + value: Record | Date; + valueOnly?: boolean; +}) => { + if (value instanceof Date) { + return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; + } + + if (style !== 'deepObject' && !explode) { + let values: string[] = []; + Object.entries(value).forEach(([key, v]) => { + values = [...values, key, allowReserved ? (v as string) : encodeURIComponent(v as string)]; + }); + const joinedValues = values.join(','); + switch (style) { + case 'form': + return `${name}=${joinedValues}`; + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + default: + return joinedValues; + } + } + + const separator = separatorObjectExplode(style); + const joinedValues = Object.entries(value) + .map(([key, v]) => + serializePrimitiveParam({ + allowReserved, + name: style === 'deepObject' ? `${name}[${key}]` : key, + value: v as string, + }), + ) + .join(separator); + return style === 'label' || style === 'matrix' ? separator + joinedValues : joinedValues; +}; diff --git a/cmd/gc/dashboard/web/src/generated/core/queryKeySerializer.gen.ts b/cmd/gc/dashboard/web/src/generated/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..5000df606f --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/core/queryKeySerializer.gen.ts @@ -0,0 +1,117 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if (value === undefined || typeof value === 'function' || typeof value === 'symbol') { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b)); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => { + if (value === null) { + return null; + } + + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + return value; + } + + if (value === undefined || typeof value === 'function' || typeof value === 'symbol') { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if (typeof URLSearchParams !== 'undefined' && value instanceof URLSearchParams) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/cmd/gc/dashboard/web/src/generated/core/serverSentEvents.gen.ts b/cmd/gc/dashboard/web/src/generated/core/serverSentEvents.gen.ts new file mode 100644 index 0000000000..ddf3c4d13a --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/core/serverSentEvents.gen.ts @@ -0,0 +1,242 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Config } from './types.gen'; + +export type ServerSentEventsOptions = Omit & + Pick & { + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch; + /** + * Implementing clients can call request interceptors inside this hook. + */ + onRequest?: (url: string, init: RequestInit) => Promise; + /** + * Callback invoked when a network or parsing error occurs during streaming. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param error The error that occurred. + */ + onSseError?: (error: unknown) => void; + /** + * Callback invoked when an event is streamed from the server. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param event Event streamed from the server. + * @returns Nothing (void). + */ + onSseEvent?: (event: StreamEvent) => void; + serializedBody?: RequestInit['body']; + /** + * Default retry delay in milliseconds. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 3000 + */ + sseDefaultRetryDelay?: number; + /** + * Maximum number of retry attempts before giving up. + */ + sseMaxRetryAttempts?: number; + /** + * Maximum retry delay in milliseconds. + * + * Applies only when exponential backoff is used. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 30000 + */ + sseMaxRetryDelay?: number; + /** + * Optional sleep function for retry backoff. + * + * Defaults to using `setTimeout`. + */ + sseSleepFn?: (ms: number) => Promise; + url: string; + }; + +export interface StreamEvent { + data: TData; + event?: string; + id?: string; + retry?: number; +} + +export type ServerSentEventsResult = { + stream: AsyncGenerator< + TData extends Record ? TData[keyof TData] : TData, + TReturn, + TNext + >; +}; + +export function createSseClient({ + onRequest, + onSseError, + onSseEvent, + responseTransformer, + responseValidator, + sseDefaultRetryDelay, + sseMaxRetryAttempts, + sseMaxRetryDelay, + sseSleepFn, + url, + ...options +}: ServerSentEventsOptions): ServerSentEventsResult { + let lastEventId: string | undefined; + + const sleep = sseSleepFn ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms))); + + const createStream = async function* () { + let retryDelay: number = sseDefaultRetryDelay ?? 3000; + let attempt = 0; + const signal = options.signal ?? new AbortController().signal; + + while (true) { + if (signal.aborted) break; + + attempt++; + + const headers = + options.headers instanceof Headers + ? options.headers + : new Headers(options.headers as Record | undefined); + + if (lastEventId !== undefined) { + headers.set('Last-Event-ID', lastEventId); + } + + try { + const requestInit: RequestInit = { + redirect: 'follow', + ...options, + body: options.serializedBody, + headers, + signal, + }; + let request = new Request(url, requestInit); + if (onRequest) { + request = await onRequest(url, requestInit); + } + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = options.fetch ?? globalThis.fetch; + const response = await _fetch(request); + + if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`); + + if (!response.body) throw new Error('No body in SSE response'); + + const reader = response.body.pipeThrough(new TextDecoderStream()).getReader(); + + let buffer = ''; + + const abortHandler = () => { + try { + reader.cancel(); + } catch { + // noop + } + }; + + signal.addEventListener('abort', abortHandler); + + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buffer += value; + buffer = buffer.replace(/\r\n?/g, '\n'); // normalize line endings + + const chunks = buffer.split('\n\n'); + buffer = chunks.pop() ?? ''; + + for (const chunk of chunks) { + const lines = chunk.split('\n'); + const dataLines: Array = []; + let eventName: string | undefined; + + for (const line of lines) { + if (line.startsWith('data:')) { + dataLines.push(line.replace(/^data:\s*/, '')); + } else if (line.startsWith('event:')) { + eventName = line.replace(/^event:\s*/, ''); + } else if (line.startsWith('id:')) { + lastEventId = line.replace(/^id:\s*/, ''); + } else if (line.startsWith('retry:')) { + const parsed = Number.parseInt(line.replace(/^retry:\s*/, ''), 10); + if (!Number.isNaN(parsed)) { + retryDelay = parsed; + } + } + } + + let data: unknown; + let parsedJson = false; + + if (dataLines.length) { + const rawData = dataLines.join('\n'); + try { + data = JSON.parse(rawData); + parsedJson = true; + } catch { + data = rawData; + } + } + + if (parsedJson) { + if (responseValidator) { + await responseValidator(data); + } + + if (responseTransformer) { + data = await responseTransformer(data); + } + } + + onSseEvent?.({ + data, + event: eventName, + id: lastEventId, + retry: retryDelay, + }); + + if (dataLines.length) { + yield data as any; + } + } + } + } finally { + signal.removeEventListener('abort', abortHandler); + reader.releaseLock(); + } + + break; // exit loop on normal completion + } catch (error) { + // connection failed or aborted; retry after delay + onSseError?.(error); + + if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) { + break; // stop after firing error + } + + // exponential backoff: double retry each attempt, cap at 30s + const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000); + await sleep(backoff); + } + } + }; + + const stream = createStream(); + + return { stream }; +} diff --git a/cmd/gc/dashboard/web/src/generated/core/types.gen.ts b/cmd/gc/dashboard/web/src/generated/core/types.gen.ts new file mode 100644 index 0000000000..9efe71d4c1 --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/core/types.gen.ts @@ -0,0 +1,104 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth, AuthToken } from './auth.gen'; +import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from './bodySerializer.gen'; + +export type HttpMethod = + | 'connect' + | 'delete' + | 'get' + | 'head' + | 'options' + | 'patch' + | 'post' + | 'put' + | 'trace'; + +export type Client< + RequestFn = never, + Config = unknown, + MethodFn = never, + BuildUrlFn = never, + SseFn = never, +> = { + /** + * Returns the final request URL. + */ + buildUrl: BuildUrlFn; + getConfig: () => Config; + request: RequestFn; + setConfig: (config: Config) => Config; +} & { + [K in HttpMethod]: MethodFn; +} & ([SseFn] extends [never] ? { sse?: never } : { sse: { [K in HttpMethod]: SseFn } }); + +export interface Config { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: + | RequestInit['headers'] + | Record< + string, + string | number | boolean | (string | number | boolean)[] | null | undefined | unknown + >; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: Uppercase; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * This method will have no effect if the native `paramsSerializer()` Axios + * API function is used. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function validating request data. This is useful if you want to ensure + * the request conforms to the desired shape, so it can be safely sent to + * the server. + */ + requestValidator?: (data: unknown) => Promise; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g., converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; +} + +type IsExactlyNeverOrNeverUndefined = [T] extends [never] + ? true + : [T] extends [never | undefined] + ? [undefined] extends [T] + ? false + : true + : false; + +export type OmitNever> = { + [K in keyof T as IsExactlyNeverOrNeverUndefined extends true ? never : K]: T[K]; +}; diff --git a/cmd/gc/dashboard/web/src/generated/core/utils.gen.ts b/cmd/gc/dashboard/web/src/generated/core/utils.gen.ts new file mode 100644 index 0000000000..9a4fec7830 --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/core/utils.gen.ts @@ -0,0 +1,140 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { BodySerializer, QuerySerializer } from './bodySerializer.gen'; +import { + type ArraySeparatorStyle, + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from './pathSerializer.gen'; + +export interface PathSerializer { + path: Record; + url: string; +} + +export const PATH_PARAM_RE = /\{[^{}]+\}/g; + +export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { + let url = _url; + const matches = _url.match(PATH_PARAM_RE); + if (matches) { + for (const match of matches) { + let explode = false; + let name = match.substring(1, match.length - 1); + let style: ArraySeparatorStyle = 'simple'; + + if (name.endsWith('*')) { + explode = true; + name = name.substring(0, name.length - 1); + } + + if (name.startsWith('.')) { + name = name.substring(1); + style = 'label'; + } else if (name.startsWith(';')) { + name = name.substring(1); + style = 'matrix'; + } + + const value = path[name]; + + if (value === undefined || value === null) { + continue; + } + + if (Array.isArray(value)) { + url = url.replace(match, serializeArrayParam({ explode, name, style, value })); + continue; + } + + if (typeof value === 'object') { + url = url.replace( + match, + serializeObjectParam({ + explode, + name, + style, + value: value as Record, + valueOnly: true, + }), + ); + continue; + } + + if (style === 'matrix') { + url = url.replace( + match, + `;${serializePrimitiveParam({ + name, + value: value as string, + })}`, + ); + continue; + } + + const replaceValue = encodeURIComponent( + style === 'label' ? `.${value as string}` : (value as string), + ); + url = url.replace(match, replaceValue); + } + } + return url; +}; + +export const getUrl = ({ + baseUrl, + path, + query, + querySerializer, + url: _url, +}: { + baseUrl?: string; + path?: Record; + query?: Record; + querySerializer: QuerySerializer; + url: string; +}) => { + const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; + let url = (baseUrl ?? '') + pathUrl; + if (path) { + url = defaultPathSerializer({ path, url }); + } + let search = query ? querySerializer(query) : ''; + if (search.startsWith('?')) { + search = search.substring(1); + } + if (search) { + url += `?${search}`; + } + return url; +}; + +export function getValidRequestBody(options: { + body?: unknown; + bodySerializer?: BodySerializer | null; + serializedBody?: unknown; +}) { + const hasBody = options.body !== undefined; + const isSerializedBody = hasBody && options.bodySerializer; + + if (isSerializedBody) { + if ('serializedBody' in options) { + const hasSerializedBody = + options.serializedBody !== undefined && options.serializedBody !== ''; + + return hasSerializedBody ? options.serializedBody : null; + } + + // not all clients implement a serializedBody property (i.e., client-axios) + return options.body !== '' ? options.body : null; + } + + // plain/text body + if (hasBody) { + return options.body; + } + + // no body was provided + return undefined; +} diff --git a/cmd/gc/dashboard/web/src/generated/index.ts b/cmd/gc/dashboard/web/src/generated/index.ts new file mode 100644 index 0000000000..47eed4b632 --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/index.ts @@ -0,0 +1,4 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export { createAgent, createBead, createConvoy, createProvider, createRig, createSession, deleteV0CityByCityNameAgentByBase, deleteV0CityByCityNameAgentByDirByBase, deleteV0CityByCityNameBeadById, deleteV0CityByCityNameConvoyById, deleteV0CityByCityNameExtmsgAdapters, deleteV0CityByCityNameExtmsgParticipants, deleteV0CityByCityNameMailById, deleteV0CityByCityNamePatchesAgentByBase, deleteV0CityByCityNamePatchesAgentByDirByBase, deleteV0CityByCityNamePatchesProviderByName, deleteV0CityByCityNamePatchesRigByName, deleteV0CityByCityNameProviderByName, deleteV0CityByCityNameRigByName, deleteV0CityByCityNameWorkflowByWorkflowId, emitEvent, ensureExtmsgGroup, getHealth, getV0Cities, getV0CityByCityName, getV0CityByCityNameAgentByBase, getV0CityByCityNameAgentByBaseOutput, getV0CityByCityNameAgentByDirByBase, getV0CityByCityNameAgentByDirByBaseOutput, getV0CityByCityNameAgents, getV0CityByCityNameBeadById, getV0CityByCityNameBeadByIdDeps, getV0CityByCityNameBeads, getV0CityByCityNameBeadsGraphByRootId, getV0CityByCityNameBeadsReady, getV0CityByCityNameConfig, getV0CityByCityNameConfigExplain, getV0CityByCityNameConfigValidate, getV0CityByCityNameConvoyById, getV0CityByCityNameConvoyByIdCheck, getV0CityByCityNameConvoys, getV0CityByCityNameEvents, getV0CityByCityNameExtmsgAdapters, getV0CityByCityNameExtmsgBindings, getV0CityByCityNameExtmsgGroups, getV0CityByCityNameExtmsgTranscript, getV0CityByCityNameFormulaByName, getV0CityByCityNameFormulas, getV0CityByCityNameFormulasByName, getV0CityByCityNameFormulasByNameRuns, getV0CityByCityNameFormulasFeed, getV0CityByCityNameHealth, getV0CityByCityNameMail, getV0CityByCityNameMailById, getV0CityByCityNameMailCount, getV0CityByCityNameMailThreadById, getV0CityByCityNameOrderByName, getV0CityByCityNameOrderHistoryByBeadId, getV0CityByCityNameOrders, getV0CityByCityNameOrdersCheck, getV0CityByCityNameOrdersFeed, getV0CityByCityNameOrdersHistory, getV0CityByCityNamePacks, getV0CityByCityNamePatchesAgentByBase, getV0CityByCityNamePatchesAgentByDirByBase, getV0CityByCityNamePatchesAgents, getV0CityByCityNamePatchesProviderByName, getV0CityByCityNamePatchesProviders, getV0CityByCityNamePatchesRigByName, getV0CityByCityNamePatchesRigs, getV0CityByCityNameProviderByName, getV0CityByCityNameProviderReadiness, getV0CityByCityNameProviders, getV0CityByCityNameProvidersPublic, getV0CityByCityNameReadiness, getV0CityByCityNameRigByName, getV0CityByCityNameRigs, getV0CityByCityNameServiceByName, getV0CityByCityNameServices, getV0CityByCityNameSessionById, getV0CityByCityNameSessionByIdAgents, getV0CityByCityNameSessionByIdAgentsByAgentId, getV0CityByCityNameSessionByIdPending, getV0CityByCityNameSessionByIdTranscript, getV0CityByCityNameSessions, getV0CityByCityNameStatus, getV0CityByCityNameWorkflowByWorkflowId, getV0Events, getV0ProviderReadiness, getV0Readiness, type Options, patchV0CityByCityName, patchV0CityByCityNameAgentByBase, patchV0CityByCityNameAgentByDirByBase, patchV0CityByCityNameBeadById, patchV0CityByCityNameProviderByName, patchV0CityByCityNameRigByName, patchV0CityByCityNameSessionById, postV0City, postV0CityByCityNameAgentByBaseByAction, postV0CityByCityNameAgentByDirByBaseByAction, postV0CityByCityNameBeadByIdAssign, postV0CityByCityNameBeadByIdClose, postV0CityByCityNameBeadByIdReopen, postV0CityByCityNameBeadByIdUpdate, postV0CityByCityNameConvoyByIdAdd, postV0CityByCityNameConvoyByIdClose, postV0CityByCityNameConvoyByIdRemove, postV0CityByCityNameExtmsgBind, postV0CityByCityNameExtmsgInbound, postV0CityByCityNameExtmsgOutbound, postV0CityByCityNameExtmsgParticipants, postV0CityByCityNameExtmsgTranscriptAck, postV0CityByCityNameExtmsgUnbind, postV0CityByCityNameFormulasByNamePreview, postV0CityByCityNameMailByIdArchive, postV0CityByCityNameMailByIdMarkUnread, postV0CityByCityNameMailByIdRead, postV0CityByCityNameOrderByNameDisable, postV0CityByCityNameOrderByNameEnable, postV0CityByCityNameRigByNameByAction, postV0CityByCityNameServiceByNameRestart, postV0CityByCityNameSessionByIdClose, postV0CityByCityNameSessionByIdKill, postV0CityByCityNameSessionByIdRename, postV0CityByCityNameSessionByIdStop, postV0CityByCityNameSessionByIdSuspend, postV0CityByCityNameSessionByIdWake, postV0CityByCityNameSling, postV0CityByCityNameUnregister, putV0CityByCityNamePatchesAgents, putV0CityByCityNamePatchesProviders, putV0CityByCityNamePatchesRigs, registerExtmsgAdapter, replyMail, respondSession, sendMail, sendSessionMessage, streamAgentOutput, streamAgentOutputQualified, streamEvents, streamSession, streamSupervisorEvents, submitSession } from './sdk.gen'; +export type { AdapterCapabilities, AdapterEventPayload, AgentCreatedOutputBody, AgentCreateInputBody, AgentMapping, AgentOutputResponse, AgentPatch, AgentPatchSetInputBody, AgentResponse, AgentUpdateInputBody, AgentUpdateQualifiedInputBody, AnnotatedAgentResponse, AnnotatedProviderResponse, Bead, BeadAssignInputBody, BeadCreateInputBody, BeadDepsResponse, BeadEventPayload, BeadGraphResponse, BeadUpdateBody, BindingStatus, BoundEventPayload, CityCreateRequest, CityCreateResponse, CityGetResponse, CityInfo, CityLifecyclePayload, CityPatchInputBody, CityUnregisterResponse, ClientOptions, ConfigAgentResponse, ConfigExplainPatches, ConfigExplainResponse, ConfigPatchesResponse, ConfigResponse, ConfigRigResponse, ConfigValidateOutputBody, ConversationGroupParticipant, ConversationGroupRecord, ConversationKind, ConversationRef, ConversationTranscriptRecord, ConvoyAddInputBody, ConvoyCheckResponse, ConvoyCreateInputBody, ConvoyGetResponse, ConvoyProgress, ConvoyRemoveInputBody, CreateAgentData, CreateAgentError, CreateAgentErrors, CreateAgentResponse, CreateAgentResponses, CreateBeadData, CreateBeadError, CreateBeadErrors, CreateBeadResponse, CreateBeadResponses, CreateConvoyData, CreateConvoyError, CreateConvoyErrors, CreateConvoyResponse, CreateConvoyResponses, CreateProviderData, CreateProviderError, CreateProviderErrors, CreateProviderResponse, CreateProviderResponses, CreateRigData, CreateRigError, CreateRigErrors, CreateRigResponse, CreateRigResponses, CreateSessionData, CreateSessionError, CreateSessionErrors, CreateSessionResponse, CreateSessionResponses, DeleteV0CityByCityNameAgentByBaseData, DeleteV0CityByCityNameAgentByBaseError, DeleteV0CityByCityNameAgentByBaseErrors, DeleteV0CityByCityNameAgentByBaseResponse, DeleteV0CityByCityNameAgentByBaseResponses, DeleteV0CityByCityNameAgentByDirByBaseData, DeleteV0CityByCityNameAgentByDirByBaseError, DeleteV0CityByCityNameAgentByDirByBaseErrors, DeleteV0CityByCityNameAgentByDirByBaseResponse, DeleteV0CityByCityNameAgentByDirByBaseResponses, DeleteV0CityByCityNameBeadByIdData, DeleteV0CityByCityNameBeadByIdError, DeleteV0CityByCityNameBeadByIdErrors, DeleteV0CityByCityNameBeadByIdResponse, DeleteV0CityByCityNameBeadByIdResponses, DeleteV0CityByCityNameConvoyByIdData, DeleteV0CityByCityNameConvoyByIdError, DeleteV0CityByCityNameConvoyByIdErrors, DeleteV0CityByCityNameConvoyByIdResponse, DeleteV0CityByCityNameConvoyByIdResponses, DeleteV0CityByCityNameExtmsgAdaptersData, DeleteV0CityByCityNameExtmsgAdaptersError, DeleteV0CityByCityNameExtmsgAdaptersErrors, DeleteV0CityByCityNameExtmsgAdaptersResponse, DeleteV0CityByCityNameExtmsgAdaptersResponses, DeleteV0CityByCityNameExtmsgParticipantsData, DeleteV0CityByCityNameExtmsgParticipantsError, DeleteV0CityByCityNameExtmsgParticipantsErrors, DeleteV0CityByCityNameExtmsgParticipantsResponse, DeleteV0CityByCityNameExtmsgParticipantsResponses, DeleteV0CityByCityNameMailByIdData, DeleteV0CityByCityNameMailByIdError, DeleteV0CityByCityNameMailByIdErrors, DeleteV0CityByCityNameMailByIdResponse, DeleteV0CityByCityNameMailByIdResponses, DeleteV0CityByCityNamePatchesAgentByBaseData, DeleteV0CityByCityNamePatchesAgentByBaseError, DeleteV0CityByCityNamePatchesAgentByBaseErrors, DeleteV0CityByCityNamePatchesAgentByBaseResponse, DeleteV0CityByCityNamePatchesAgentByBaseResponses, DeleteV0CityByCityNamePatchesAgentByDirByBaseData, DeleteV0CityByCityNamePatchesAgentByDirByBaseError, DeleteV0CityByCityNamePatchesAgentByDirByBaseErrors, DeleteV0CityByCityNamePatchesAgentByDirByBaseResponse, DeleteV0CityByCityNamePatchesAgentByDirByBaseResponses, DeleteV0CityByCityNamePatchesProviderByNameData, DeleteV0CityByCityNamePatchesProviderByNameError, DeleteV0CityByCityNamePatchesProviderByNameErrors, DeleteV0CityByCityNamePatchesProviderByNameResponse, DeleteV0CityByCityNamePatchesProviderByNameResponses, DeleteV0CityByCityNamePatchesRigByNameData, DeleteV0CityByCityNamePatchesRigByNameError, DeleteV0CityByCityNamePatchesRigByNameErrors, DeleteV0CityByCityNamePatchesRigByNameResponse, DeleteV0CityByCityNamePatchesRigByNameResponses, DeleteV0CityByCityNameProviderByNameData, DeleteV0CityByCityNameProviderByNameError, DeleteV0CityByCityNameProviderByNameErrors, DeleteV0CityByCityNameProviderByNameResponse, DeleteV0CityByCityNameProviderByNameResponses, DeleteV0CityByCityNameRigByNameData, DeleteV0CityByCityNameRigByNameError, DeleteV0CityByCityNameRigByNameErrors, DeleteV0CityByCityNameRigByNameResponse, DeleteV0CityByCityNameRigByNameResponses, DeleteV0CityByCityNameWorkflowByWorkflowIdData, DeleteV0CityByCityNameWorkflowByWorkflowIdError, DeleteV0CityByCityNameWorkflowByWorkflowIdErrors, DeleteV0CityByCityNameWorkflowByWorkflowIdResponse, DeleteV0CityByCityNameWorkflowByWorkflowIdResponses, DeliveryContextRecord, Dep, EmitEventData, EmitEventError, EmitEventErrors, EmitEventResponse, EmitEventResponses, EnsureExtmsgGroupData, EnsureExtmsgGroupError, EnsureExtmsgGroupErrors, EnsureExtmsgGroupResponse, EnsureExtmsgGroupResponses, ErrorDetail, ErrorModel, EventEmitOutputBody, EventEmitRequest, EventPayload, EventStreamEnvelope, ExternalActor, ExternalAttachment, ExternalInboundMessage, ExtmsgAdapterInfo, ExtMsgAdapterRegisterInputBody, ExtMsgAdapterRegisterOutputBody, ExtMsgAdapterUnregisterInputBody, ExtMsgBindInputBody, ExtMsgGroupEnsureInputBody, ExtMsgInboundInputBody, ExtMsgOutboundInputBody, ExtMsgParticipantRemoveInputBody, ExtMsgParticipantUpsertInputBody, ExtMsgTranscriptAckInputBody, ExtMsgUnbindBody, ExtMsgUnbindInputBody, FanoutPolicy, FormulaDetailResponse, FormulaFeedBody, FormulaListBody, FormulaPreviewBody, FormulaPreviewEdgeResponse, FormulaPreviewNodeResponse, FormulaPreviewResponse, FormulaRecentRunResponse, FormulaRunsResponse, FormulaStepResponse, FormulaSummaryResponse, FormulaVarDefResponse, GetHealthData, GetHealthError, GetHealthErrors, GetHealthResponse, GetHealthResponses, GetV0CitiesData, GetV0CitiesError, GetV0CitiesErrors, GetV0CitiesResponse, GetV0CitiesResponses, GetV0CityByCityNameAgentByBaseData, GetV0CityByCityNameAgentByBaseError, GetV0CityByCityNameAgentByBaseErrors, GetV0CityByCityNameAgentByBaseOutputData, GetV0CityByCityNameAgentByBaseOutputError, GetV0CityByCityNameAgentByBaseOutputErrors, GetV0CityByCityNameAgentByBaseOutputResponse, GetV0CityByCityNameAgentByBaseOutputResponses, GetV0CityByCityNameAgentByBaseResponse, GetV0CityByCityNameAgentByBaseResponses, GetV0CityByCityNameAgentByDirByBaseData, GetV0CityByCityNameAgentByDirByBaseError, GetV0CityByCityNameAgentByDirByBaseErrors, GetV0CityByCityNameAgentByDirByBaseOutputData, GetV0CityByCityNameAgentByDirByBaseOutputError, GetV0CityByCityNameAgentByDirByBaseOutputErrors, GetV0CityByCityNameAgentByDirByBaseOutputResponse, GetV0CityByCityNameAgentByDirByBaseOutputResponses, GetV0CityByCityNameAgentByDirByBaseResponse, GetV0CityByCityNameAgentByDirByBaseResponses, GetV0CityByCityNameAgentsData, GetV0CityByCityNameAgentsError, GetV0CityByCityNameAgentsErrors, GetV0CityByCityNameAgentsResponse, GetV0CityByCityNameAgentsResponses, GetV0CityByCityNameBeadByIdData, GetV0CityByCityNameBeadByIdDepsData, GetV0CityByCityNameBeadByIdDepsError, GetV0CityByCityNameBeadByIdDepsErrors, GetV0CityByCityNameBeadByIdDepsResponse, GetV0CityByCityNameBeadByIdDepsResponses, GetV0CityByCityNameBeadByIdError, GetV0CityByCityNameBeadByIdErrors, GetV0CityByCityNameBeadByIdResponse, GetV0CityByCityNameBeadByIdResponses, GetV0CityByCityNameBeadsData, GetV0CityByCityNameBeadsError, GetV0CityByCityNameBeadsErrors, GetV0CityByCityNameBeadsGraphByRootIdData, GetV0CityByCityNameBeadsGraphByRootIdError, GetV0CityByCityNameBeadsGraphByRootIdErrors, GetV0CityByCityNameBeadsGraphByRootIdResponse, GetV0CityByCityNameBeadsGraphByRootIdResponses, GetV0CityByCityNameBeadsReadyData, GetV0CityByCityNameBeadsReadyError, GetV0CityByCityNameBeadsReadyErrors, GetV0CityByCityNameBeadsReadyResponse, GetV0CityByCityNameBeadsReadyResponses, GetV0CityByCityNameBeadsResponse, GetV0CityByCityNameBeadsResponses, GetV0CityByCityNameConfigData, GetV0CityByCityNameConfigError, GetV0CityByCityNameConfigErrors, GetV0CityByCityNameConfigExplainData, GetV0CityByCityNameConfigExplainError, GetV0CityByCityNameConfigExplainErrors, GetV0CityByCityNameConfigExplainResponse, GetV0CityByCityNameConfigExplainResponses, GetV0CityByCityNameConfigResponse, GetV0CityByCityNameConfigResponses, GetV0CityByCityNameConfigValidateData, GetV0CityByCityNameConfigValidateError, GetV0CityByCityNameConfigValidateErrors, GetV0CityByCityNameConfigValidateResponse, GetV0CityByCityNameConfigValidateResponses, GetV0CityByCityNameConvoyByIdCheckData, GetV0CityByCityNameConvoyByIdCheckError, GetV0CityByCityNameConvoyByIdCheckErrors, GetV0CityByCityNameConvoyByIdCheckResponse, GetV0CityByCityNameConvoyByIdCheckResponses, GetV0CityByCityNameConvoyByIdData, GetV0CityByCityNameConvoyByIdError, GetV0CityByCityNameConvoyByIdErrors, GetV0CityByCityNameConvoyByIdResponse, GetV0CityByCityNameConvoyByIdResponses, GetV0CityByCityNameConvoysData, GetV0CityByCityNameConvoysError, GetV0CityByCityNameConvoysErrors, GetV0CityByCityNameConvoysResponse, GetV0CityByCityNameConvoysResponses, GetV0CityByCityNameData, GetV0CityByCityNameError, GetV0CityByCityNameErrors, GetV0CityByCityNameEventsData, GetV0CityByCityNameEventsError, GetV0CityByCityNameEventsErrors, GetV0CityByCityNameEventsResponse, GetV0CityByCityNameEventsResponses, GetV0CityByCityNameExtmsgAdaptersData, GetV0CityByCityNameExtmsgAdaptersError, GetV0CityByCityNameExtmsgAdaptersErrors, GetV0CityByCityNameExtmsgAdaptersResponse, GetV0CityByCityNameExtmsgAdaptersResponses, GetV0CityByCityNameExtmsgBindingsData, GetV0CityByCityNameExtmsgBindingsError, GetV0CityByCityNameExtmsgBindingsErrors, GetV0CityByCityNameExtmsgBindingsResponse, GetV0CityByCityNameExtmsgBindingsResponses, GetV0CityByCityNameExtmsgGroupsData, GetV0CityByCityNameExtmsgGroupsError, GetV0CityByCityNameExtmsgGroupsErrors, GetV0CityByCityNameExtmsgGroupsResponse, GetV0CityByCityNameExtmsgGroupsResponses, GetV0CityByCityNameExtmsgTranscriptData, GetV0CityByCityNameExtmsgTranscriptError, GetV0CityByCityNameExtmsgTranscriptErrors, GetV0CityByCityNameExtmsgTranscriptResponse, GetV0CityByCityNameExtmsgTranscriptResponses, GetV0CityByCityNameFormulaByNameData, GetV0CityByCityNameFormulaByNameError, GetV0CityByCityNameFormulaByNameErrors, GetV0CityByCityNameFormulaByNameResponse, GetV0CityByCityNameFormulaByNameResponses, GetV0CityByCityNameFormulasByNameData, GetV0CityByCityNameFormulasByNameError, GetV0CityByCityNameFormulasByNameErrors, GetV0CityByCityNameFormulasByNameResponse, GetV0CityByCityNameFormulasByNameResponses, GetV0CityByCityNameFormulasByNameRunsData, GetV0CityByCityNameFormulasByNameRunsError, GetV0CityByCityNameFormulasByNameRunsErrors, GetV0CityByCityNameFormulasByNameRunsResponse, GetV0CityByCityNameFormulasByNameRunsResponses, GetV0CityByCityNameFormulasData, GetV0CityByCityNameFormulasError, GetV0CityByCityNameFormulasErrors, GetV0CityByCityNameFormulasFeedData, GetV0CityByCityNameFormulasFeedError, GetV0CityByCityNameFormulasFeedErrors, GetV0CityByCityNameFormulasFeedResponse, GetV0CityByCityNameFormulasFeedResponses, GetV0CityByCityNameFormulasResponse, GetV0CityByCityNameFormulasResponses, GetV0CityByCityNameHealthData, GetV0CityByCityNameHealthError, GetV0CityByCityNameHealthErrors, GetV0CityByCityNameHealthResponse, GetV0CityByCityNameHealthResponses, GetV0CityByCityNameMailByIdData, GetV0CityByCityNameMailByIdError, GetV0CityByCityNameMailByIdErrors, GetV0CityByCityNameMailByIdResponse, GetV0CityByCityNameMailByIdResponses, GetV0CityByCityNameMailCountData, GetV0CityByCityNameMailCountError, GetV0CityByCityNameMailCountErrors, GetV0CityByCityNameMailCountResponse, GetV0CityByCityNameMailCountResponses, GetV0CityByCityNameMailData, GetV0CityByCityNameMailError, GetV0CityByCityNameMailErrors, GetV0CityByCityNameMailResponse, GetV0CityByCityNameMailResponses, GetV0CityByCityNameMailThreadByIdData, GetV0CityByCityNameMailThreadByIdError, GetV0CityByCityNameMailThreadByIdErrors, GetV0CityByCityNameMailThreadByIdResponse, GetV0CityByCityNameMailThreadByIdResponses, GetV0CityByCityNameOrderByNameData, GetV0CityByCityNameOrderByNameError, GetV0CityByCityNameOrderByNameErrors, GetV0CityByCityNameOrderByNameResponse, GetV0CityByCityNameOrderByNameResponses, GetV0CityByCityNameOrderHistoryByBeadIdData, GetV0CityByCityNameOrderHistoryByBeadIdError, GetV0CityByCityNameOrderHistoryByBeadIdErrors, GetV0CityByCityNameOrderHistoryByBeadIdResponse, GetV0CityByCityNameOrderHistoryByBeadIdResponses, GetV0CityByCityNameOrdersCheckData, GetV0CityByCityNameOrdersCheckError, GetV0CityByCityNameOrdersCheckErrors, GetV0CityByCityNameOrdersCheckResponse, GetV0CityByCityNameOrdersCheckResponses, GetV0CityByCityNameOrdersData, GetV0CityByCityNameOrdersError, GetV0CityByCityNameOrdersErrors, GetV0CityByCityNameOrdersFeedData, GetV0CityByCityNameOrdersFeedError, GetV0CityByCityNameOrdersFeedErrors, GetV0CityByCityNameOrdersFeedResponse, GetV0CityByCityNameOrdersFeedResponses, GetV0CityByCityNameOrdersHistoryData, GetV0CityByCityNameOrdersHistoryError, GetV0CityByCityNameOrdersHistoryErrors, GetV0CityByCityNameOrdersHistoryResponse, GetV0CityByCityNameOrdersHistoryResponses, GetV0CityByCityNameOrdersResponse, GetV0CityByCityNameOrdersResponses, GetV0CityByCityNamePacksData, GetV0CityByCityNamePacksError, GetV0CityByCityNamePacksErrors, GetV0CityByCityNamePacksResponse, GetV0CityByCityNamePacksResponses, GetV0CityByCityNamePatchesAgentByBaseData, GetV0CityByCityNamePatchesAgentByBaseError, GetV0CityByCityNamePatchesAgentByBaseErrors, GetV0CityByCityNamePatchesAgentByBaseResponse, GetV0CityByCityNamePatchesAgentByBaseResponses, GetV0CityByCityNamePatchesAgentByDirByBaseData, GetV0CityByCityNamePatchesAgentByDirByBaseError, GetV0CityByCityNamePatchesAgentByDirByBaseErrors, GetV0CityByCityNamePatchesAgentByDirByBaseResponse, GetV0CityByCityNamePatchesAgentByDirByBaseResponses, GetV0CityByCityNamePatchesAgentsData, GetV0CityByCityNamePatchesAgentsError, GetV0CityByCityNamePatchesAgentsErrors, GetV0CityByCityNamePatchesAgentsResponse, GetV0CityByCityNamePatchesAgentsResponses, GetV0CityByCityNamePatchesProviderByNameData, GetV0CityByCityNamePatchesProviderByNameError, GetV0CityByCityNamePatchesProviderByNameErrors, GetV0CityByCityNamePatchesProviderByNameResponse, GetV0CityByCityNamePatchesProviderByNameResponses, GetV0CityByCityNamePatchesProvidersData, GetV0CityByCityNamePatchesProvidersError, GetV0CityByCityNamePatchesProvidersErrors, GetV0CityByCityNamePatchesProvidersResponse, GetV0CityByCityNamePatchesProvidersResponses, GetV0CityByCityNamePatchesRigByNameData, GetV0CityByCityNamePatchesRigByNameError, GetV0CityByCityNamePatchesRigByNameErrors, GetV0CityByCityNamePatchesRigByNameResponse, GetV0CityByCityNamePatchesRigByNameResponses, GetV0CityByCityNamePatchesRigsData, GetV0CityByCityNamePatchesRigsError, GetV0CityByCityNamePatchesRigsErrors, GetV0CityByCityNamePatchesRigsResponse, GetV0CityByCityNamePatchesRigsResponses, GetV0CityByCityNameProviderByNameData, GetV0CityByCityNameProviderByNameError, GetV0CityByCityNameProviderByNameErrors, GetV0CityByCityNameProviderByNameResponse, GetV0CityByCityNameProviderByNameResponses, GetV0CityByCityNameProviderReadinessData, GetV0CityByCityNameProviderReadinessError, GetV0CityByCityNameProviderReadinessErrors, GetV0CityByCityNameProviderReadinessResponse, GetV0CityByCityNameProviderReadinessResponses, GetV0CityByCityNameProvidersData, GetV0CityByCityNameProvidersError, GetV0CityByCityNameProvidersErrors, GetV0CityByCityNameProvidersPublicData, GetV0CityByCityNameProvidersPublicError, GetV0CityByCityNameProvidersPublicErrors, GetV0CityByCityNameProvidersPublicResponse, GetV0CityByCityNameProvidersPublicResponses, GetV0CityByCityNameProvidersResponse, GetV0CityByCityNameProvidersResponses, GetV0CityByCityNameReadinessData, GetV0CityByCityNameReadinessError, GetV0CityByCityNameReadinessErrors, GetV0CityByCityNameReadinessResponse, GetV0CityByCityNameReadinessResponses, GetV0CityByCityNameResponse, GetV0CityByCityNameResponses, GetV0CityByCityNameRigByNameData, GetV0CityByCityNameRigByNameError, GetV0CityByCityNameRigByNameErrors, GetV0CityByCityNameRigByNameResponse, GetV0CityByCityNameRigByNameResponses, GetV0CityByCityNameRigsData, GetV0CityByCityNameRigsError, GetV0CityByCityNameRigsErrors, GetV0CityByCityNameRigsResponse, GetV0CityByCityNameRigsResponses, GetV0CityByCityNameServiceByNameData, GetV0CityByCityNameServiceByNameError, GetV0CityByCityNameServiceByNameErrors, GetV0CityByCityNameServiceByNameResponse, GetV0CityByCityNameServiceByNameResponses, GetV0CityByCityNameServicesData, GetV0CityByCityNameServicesError, GetV0CityByCityNameServicesErrors, GetV0CityByCityNameServicesResponse, GetV0CityByCityNameServicesResponses, GetV0CityByCityNameSessionByIdAgentsByAgentIdData, GetV0CityByCityNameSessionByIdAgentsByAgentIdError, GetV0CityByCityNameSessionByIdAgentsByAgentIdErrors, GetV0CityByCityNameSessionByIdAgentsByAgentIdResponse, GetV0CityByCityNameSessionByIdAgentsByAgentIdResponses, GetV0CityByCityNameSessionByIdAgentsData, GetV0CityByCityNameSessionByIdAgentsError, GetV0CityByCityNameSessionByIdAgentsErrors, GetV0CityByCityNameSessionByIdAgentsResponse, GetV0CityByCityNameSessionByIdAgentsResponses, GetV0CityByCityNameSessionByIdData, GetV0CityByCityNameSessionByIdError, GetV0CityByCityNameSessionByIdErrors, GetV0CityByCityNameSessionByIdPendingData, GetV0CityByCityNameSessionByIdPendingError, GetV0CityByCityNameSessionByIdPendingErrors, GetV0CityByCityNameSessionByIdPendingResponse, GetV0CityByCityNameSessionByIdPendingResponses, GetV0CityByCityNameSessionByIdResponse, GetV0CityByCityNameSessionByIdResponses, GetV0CityByCityNameSessionByIdTranscriptData, GetV0CityByCityNameSessionByIdTranscriptError, GetV0CityByCityNameSessionByIdTranscriptErrors, GetV0CityByCityNameSessionByIdTranscriptResponse, GetV0CityByCityNameSessionByIdTranscriptResponses, GetV0CityByCityNameSessionsData, GetV0CityByCityNameSessionsError, GetV0CityByCityNameSessionsErrors, GetV0CityByCityNameSessionsResponse, GetV0CityByCityNameSessionsResponses, GetV0CityByCityNameStatusData, GetV0CityByCityNameStatusError, GetV0CityByCityNameStatusErrors, GetV0CityByCityNameStatusResponse, GetV0CityByCityNameStatusResponses, GetV0CityByCityNameWorkflowByWorkflowIdData, GetV0CityByCityNameWorkflowByWorkflowIdError, GetV0CityByCityNameWorkflowByWorkflowIdErrors, GetV0CityByCityNameWorkflowByWorkflowIdResponse, GetV0CityByCityNameWorkflowByWorkflowIdResponses, GetV0EventsData, GetV0EventsError, GetV0EventsErrors, GetV0EventsResponse, GetV0EventsResponses, GetV0ProviderReadinessData, GetV0ProviderReadinessError, GetV0ProviderReadinessErrors, GetV0ProviderReadinessResponse, GetV0ProviderReadinessResponses, GetV0ReadinessData, GetV0ReadinessError, GetV0ReadinessErrors, GetV0ReadinessResponse, GetV0ReadinessResponses, GitStatus, GroupCreatedEventPayload, GroupRouteDecision, HealthOutputBody, HeartbeatEvent, InboundEventPayload, InboundResult, ListBodyAgentPatch, ListBodyAgentResponse, ListBodyBead, ListBodyConversationTranscriptRecord, ListBodyExtmsgAdapterInfo, ListBodyProviderPatch, ListBodyProviderResponse, ListBodyRigPatch, ListBodyRigResponse, ListBodySessionBindingRecord, ListBodySessionResponse, ListBodyStatus, ListBodyWireEvent, LogicalNode, MailCountOutputBody, MailEventPayload, MailListBody, MailReplyInputBody, MailSendInputBody, Message, MonitorFeedItemResponse, NoPayload, OkResponseBody, OkWithIdResponseBody, OptionChoiceDto, OrderCheckListBody, OrderCheckResponse, OrderHistoryDetailResponse, OrderHistoryEntry, OrderHistoryListBody, OrderListBody, OrderResponse, OrdersFeedBody, OutboundEventPayload, OutboundResult, OutputTurn, PackListBody, PackResponse, PaginationInfo, PatchDeletedResponseBody, PatchOkResponseBody, PatchV0CityByCityNameAgentByBaseData, PatchV0CityByCityNameAgentByBaseError, PatchV0CityByCityNameAgentByBaseErrors, PatchV0CityByCityNameAgentByBaseResponse, PatchV0CityByCityNameAgentByBaseResponses, PatchV0CityByCityNameAgentByDirByBaseData, PatchV0CityByCityNameAgentByDirByBaseError, PatchV0CityByCityNameAgentByDirByBaseErrors, PatchV0CityByCityNameAgentByDirByBaseResponse, PatchV0CityByCityNameAgentByDirByBaseResponses, PatchV0CityByCityNameBeadByIdData, PatchV0CityByCityNameBeadByIdError, PatchV0CityByCityNameBeadByIdErrors, PatchV0CityByCityNameBeadByIdResponse, PatchV0CityByCityNameBeadByIdResponses, PatchV0CityByCityNameData, PatchV0CityByCityNameError, PatchV0CityByCityNameErrors, PatchV0CityByCityNameProviderByNameData, PatchV0CityByCityNameProviderByNameError, PatchV0CityByCityNameProviderByNameErrors, PatchV0CityByCityNameProviderByNameResponse, PatchV0CityByCityNameProviderByNameResponses, PatchV0CityByCityNameResponse, PatchV0CityByCityNameResponses, PatchV0CityByCityNameRigByNameData, PatchV0CityByCityNameRigByNameError, PatchV0CityByCityNameRigByNameErrors, PatchV0CityByCityNameRigByNameResponse, PatchV0CityByCityNameRigByNameResponses, PatchV0CityByCityNameSessionByIdData, PatchV0CityByCityNameSessionByIdError, PatchV0CityByCityNameSessionByIdErrors, PatchV0CityByCityNameSessionByIdResponse, PatchV0CityByCityNameSessionByIdResponses, PendingInteraction, PoolOverride, PostV0CityByCityNameAgentByBaseByActionData, PostV0CityByCityNameAgentByBaseByActionError, PostV0CityByCityNameAgentByBaseByActionErrors, PostV0CityByCityNameAgentByBaseByActionResponse, PostV0CityByCityNameAgentByBaseByActionResponses, PostV0CityByCityNameAgentByDirByBaseByActionData, PostV0CityByCityNameAgentByDirByBaseByActionError, PostV0CityByCityNameAgentByDirByBaseByActionErrors, PostV0CityByCityNameAgentByDirByBaseByActionResponse, PostV0CityByCityNameAgentByDirByBaseByActionResponses, PostV0CityByCityNameBeadByIdAssignData, PostV0CityByCityNameBeadByIdAssignError, PostV0CityByCityNameBeadByIdAssignErrors, PostV0CityByCityNameBeadByIdAssignResponse, PostV0CityByCityNameBeadByIdAssignResponses, PostV0CityByCityNameBeadByIdCloseData, PostV0CityByCityNameBeadByIdCloseError, PostV0CityByCityNameBeadByIdCloseErrors, PostV0CityByCityNameBeadByIdCloseResponse, PostV0CityByCityNameBeadByIdCloseResponses, PostV0CityByCityNameBeadByIdReopenData, PostV0CityByCityNameBeadByIdReopenError, PostV0CityByCityNameBeadByIdReopenErrors, PostV0CityByCityNameBeadByIdReopenResponse, PostV0CityByCityNameBeadByIdReopenResponses, PostV0CityByCityNameBeadByIdUpdateData, PostV0CityByCityNameBeadByIdUpdateError, PostV0CityByCityNameBeadByIdUpdateErrors, PostV0CityByCityNameBeadByIdUpdateResponse, PostV0CityByCityNameBeadByIdUpdateResponses, PostV0CityByCityNameConvoyByIdAddData, PostV0CityByCityNameConvoyByIdAddError, PostV0CityByCityNameConvoyByIdAddErrors, PostV0CityByCityNameConvoyByIdAddResponse, PostV0CityByCityNameConvoyByIdAddResponses, PostV0CityByCityNameConvoyByIdCloseData, PostV0CityByCityNameConvoyByIdCloseError, PostV0CityByCityNameConvoyByIdCloseErrors, PostV0CityByCityNameConvoyByIdCloseResponse, PostV0CityByCityNameConvoyByIdCloseResponses, PostV0CityByCityNameConvoyByIdRemoveData, PostV0CityByCityNameConvoyByIdRemoveError, PostV0CityByCityNameConvoyByIdRemoveErrors, PostV0CityByCityNameConvoyByIdRemoveResponse, PostV0CityByCityNameConvoyByIdRemoveResponses, PostV0CityByCityNameExtmsgBindData, PostV0CityByCityNameExtmsgBindError, PostV0CityByCityNameExtmsgBindErrors, PostV0CityByCityNameExtmsgBindResponse, PostV0CityByCityNameExtmsgBindResponses, PostV0CityByCityNameExtmsgInboundData, PostV0CityByCityNameExtmsgInboundError, PostV0CityByCityNameExtmsgInboundErrors, PostV0CityByCityNameExtmsgInboundResponse, PostV0CityByCityNameExtmsgInboundResponses, PostV0CityByCityNameExtmsgOutboundData, PostV0CityByCityNameExtmsgOutboundError, PostV0CityByCityNameExtmsgOutboundErrors, PostV0CityByCityNameExtmsgOutboundResponse, PostV0CityByCityNameExtmsgOutboundResponses, PostV0CityByCityNameExtmsgParticipantsData, PostV0CityByCityNameExtmsgParticipantsError, PostV0CityByCityNameExtmsgParticipantsErrors, PostV0CityByCityNameExtmsgParticipantsResponse, PostV0CityByCityNameExtmsgParticipantsResponses, PostV0CityByCityNameExtmsgTranscriptAckData, PostV0CityByCityNameExtmsgTranscriptAckError, PostV0CityByCityNameExtmsgTranscriptAckErrors, PostV0CityByCityNameExtmsgTranscriptAckResponse, PostV0CityByCityNameExtmsgTranscriptAckResponses, PostV0CityByCityNameExtmsgUnbindData, PostV0CityByCityNameExtmsgUnbindError, PostV0CityByCityNameExtmsgUnbindErrors, PostV0CityByCityNameExtmsgUnbindResponse, PostV0CityByCityNameExtmsgUnbindResponses, PostV0CityByCityNameFormulasByNamePreviewData, PostV0CityByCityNameFormulasByNamePreviewError, PostV0CityByCityNameFormulasByNamePreviewErrors, PostV0CityByCityNameFormulasByNamePreviewResponse, PostV0CityByCityNameFormulasByNamePreviewResponses, PostV0CityByCityNameMailByIdArchiveData, PostV0CityByCityNameMailByIdArchiveError, PostV0CityByCityNameMailByIdArchiveErrors, PostV0CityByCityNameMailByIdArchiveResponse, PostV0CityByCityNameMailByIdArchiveResponses, PostV0CityByCityNameMailByIdMarkUnreadData, PostV0CityByCityNameMailByIdMarkUnreadError, PostV0CityByCityNameMailByIdMarkUnreadErrors, PostV0CityByCityNameMailByIdMarkUnreadResponse, PostV0CityByCityNameMailByIdMarkUnreadResponses, PostV0CityByCityNameMailByIdReadData, PostV0CityByCityNameMailByIdReadError, PostV0CityByCityNameMailByIdReadErrors, PostV0CityByCityNameMailByIdReadResponse, PostV0CityByCityNameMailByIdReadResponses, PostV0CityByCityNameOrderByNameDisableData, PostV0CityByCityNameOrderByNameDisableError, PostV0CityByCityNameOrderByNameDisableErrors, PostV0CityByCityNameOrderByNameDisableResponse, PostV0CityByCityNameOrderByNameDisableResponses, PostV0CityByCityNameOrderByNameEnableData, PostV0CityByCityNameOrderByNameEnableError, PostV0CityByCityNameOrderByNameEnableErrors, PostV0CityByCityNameOrderByNameEnableResponse, PostV0CityByCityNameOrderByNameEnableResponses, PostV0CityByCityNameRigByNameByActionData, PostV0CityByCityNameRigByNameByActionError, PostV0CityByCityNameRigByNameByActionErrors, PostV0CityByCityNameRigByNameByActionResponse, PostV0CityByCityNameRigByNameByActionResponses, PostV0CityByCityNameServiceByNameRestartData, PostV0CityByCityNameServiceByNameRestartError, PostV0CityByCityNameServiceByNameRestartErrors, PostV0CityByCityNameServiceByNameRestartResponse, PostV0CityByCityNameServiceByNameRestartResponses, PostV0CityByCityNameSessionByIdCloseData, PostV0CityByCityNameSessionByIdCloseError, PostV0CityByCityNameSessionByIdCloseErrors, PostV0CityByCityNameSessionByIdCloseResponse, PostV0CityByCityNameSessionByIdCloseResponses, PostV0CityByCityNameSessionByIdKillData, PostV0CityByCityNameSessionByIdKillError, PostV0CityByCityNameSessionByIdKillErrors, PostV0CityByCityNameSessionByIdKillResponse, PostV0CityByCityNameSessionByIdKillResponses, PostV0CityByCityNameSessionByIdRenameData, PostV0CityByCityNameSessionByIdRenameError, PostV0CityByCityNameSessionByIdRenameErrors, PostV0CityByCityNameSessionByIdRenameResponse, PostV0CityByCityNameSessionByIdRenameResponses, PostV0CityByCityNameSessionByIdStopData, PostV0CityByCityNameSessionByIdStopError, PostV0CityByCityNameSessionByIdStopErrors, PostV0CityByCityNameSessionByIdStopResponse, PostV0CityByCityNameSessionByIdStopResponses, PostV0CityByCityNameSessionByIdSuspendData, PostV0CityByCityNameSessionByIdSuspendError, PostV0CityByCityNameSessionByIdSuspendErrors, PostV0CityByCityNameSessionByIdSuspendResponse, PostV0CityByCityNameSessionByIdSuspendResponses, PostV0CityByCityNameSessionByIdWakeData, PostV0CityByCityNameSessionByIdWakeError, PostV0CityByCityNameSessionByIdWakeErrors, PostV0CityByCityNameSessionByIdWakeResponse, PostV0CityByCityNameSessionByIdWakeResponses, PostV0CityByCityNameSlingData, PostV0CityByCityNameSlingError, PostV0CityByCityNameSlingErrors, PostV0CityByCityNameSlingResponse, PostV0CityByCityNameSlingResponses, PostV0CityByCityNameUnregisterData, PostV0CityByCityNameUnregisterError, PostV0CityByCityNameUnregisterErrors, PostV0CityByCityNameUnregisterResponse, PostV0CityByCityNameUnregisterResponses, PostV0CityData, PostV0CityError, PostV0CityErrors, PostV0CityResponse, PostV0CityResponses, ProviderCreatedOutputBody, ProviderCreateInputBody, ProviderOptionDto, ProviderPatch, ProviderPatchSetInputBody, ProviderPublicListBody, ProviderPublicResponse, ProviderReadiness, ProviderReadinessResponse, ProviderResponse, ProviderSpecJson, ProviderUpdateInputBody, PublishReceipt, PutV0CityByCityNamePatchesAgentsData, PutV0CityByCityNamePatchesAgentsError, PutV0CityByCityNamePatchesAgentsErrors, PutV0CityByCityNamePatchesAgentsResponse, PutV0CityByCityNamePatchesAgentsResponses, PutV0CityByCityNamePatchesProvidersData, PutV0CityByCityNamePatchesProvidersError, PutV0CityByCityNamePatchesProvidersErrors, PutV0CityByCityNamePatchesProvidersResponse, PutV0CityByCityNamePatchesProvidersResponses, PutV0CityByCityNamePatchesRigsData, PutV0CityByCityNamePatchesRigsError, PutV0CityByCityNamePatchesRigsErrors, PutV0CityByCityNamePatchesRigsResponse, PutV0CityByCityNamePatchesRigsResponses, ReadinessItem, ReadinessResponse, RegisterExtmsgAdapterData, RegisterExtmsgAdapterError, RegisterExtmsgAdapterErrors, RegisterExtmsgAdapterResponse, RegisterExtmsgAdapterResponses, ReplyMailData, ReplyMailError, ReplyMailErrors, ReplyMailResponse, ReplyMailResponses, RespondSessionData, RespondSessionError, RespondSessionErrors, RespondSessionResponse, RespondSessionResponses, RigActionBody, RigCreatedOutputBody, RigCreateInputBody, RigPatch, RigPatchSetInputBody, RigResponse, RigUpdateInputBody, ScopeGroup, SendMailData, SendMailError, SendMailErrors, SendMailResponse, SendMailResponses, SendSessionMessageData, SendSessionMessageError, SendSessionMessageErrors, SendSessionMessageResponse, SendSessionMessageResponses, ServiceRestartOutputBody, SessionActivityEvent, SessionAgentGetResponse, SessionAgentListResponse, SessionBindingRecord, SessionCreateBody, SessionInfo, SessionMessageInputBody, SessionMessageOutputBody, SessionPatchBody, SessionPendingResponse, SessionRawMessageFrame, SessionRenameInputBody, SessionRespondInputBody, SessionRespondOutputBody, SessionResponse, SessionStreamCommonEvent, SessionStreamMessageEvent, SessionStreamRawMessageEvent, SessionSubmitInputBody, SessionSubmitOutputBody, SessionTranscriptGetResponse, SlingInputBody, SlingResponse, Status, StatusAgentCounts, StatusBody, StatusMailCounts, StatusRigCounts, StatusWorkCounts, StreamAgentOutputData, StreamAgentOutputError, StreamAgentOutputErrors, StreamAgentOutputQualifiedData, StreamAgentOutputQualifiedError, StreamAgentOutputQualifiedErrors, StreamAgentOutputQualifiedResponse, StreamAgentOutputQualifiedResponses, StreamAgentOutputResponse, StreamAgentOutputResponses, StreamEventsData, StreamEventsError, StreamEventsErrors, StreamEventsResponse, StreamEventsResponses, StreamSessionData, StreamSessionError, StreamSessionErrors, StreamSessionResponse, StreamSessionResponses, StreamSupervisorEventsData, StreamSupervisorEventsError, StreamSupervisorEventsErrors, StreamSupervisorEventsResponse, StreamSupervisorEventsResponses, SubmissionCapabilities, SubmitIntent, SubmitSessionData, SubmitSessionError, SubmitSessionErrors, SubmitSessionResponse, SubmitSessionResponses, SupervisorCitiesOutputBody, SupervisorEventListOutputBody, SupervisorHealthOutputBody, SupervisorStartup, TaggedEventStreamEnvelope, TranscriptMessageKind, TranscriptProvenance, TypedEventStreamEnvelope, TypedEventStreamEnvelopeBeadClosed, TypedEventStreamEnvelopeBeadCreated, TypedEventStreamEnvelopeBeadUpdated, TypedEventStreamEnvelopeCityCreated, TypedEventStreamEnvelopeCityInitFailed, TypedEventStreamEnvelopeCityReady, TypedEventStreamEnvelopeCityResumed, TypedEventStreamEnvelopeCitySuspended, TypedEventStreamEnvelopeCityUnregistered, TypedEventStreamEnvelopeCityUnregisterFailed, TypedEventStreamEnvelopeCityUnregisterRequested, TypedEventStreamEnvelopeControllerStarted, TypedEventStreamEnvelopeControllerStopped, TypedEventStreamEnvelopeConvoyClosed, TypedEventStreamEnvelopeConvoyCreated, TypedEventStreamEnvelopeExtmsgAdapterAdded, TypedEventStreamEnvelopeExtmsgAdapterRemoved, TypedEventStreamEnvelopeExtmsgBound, TypedEventStreamEnvelopeExtmsgGroupCreated, TypedEventStreamEnvelopeExtmsgInbound, TypedEventStreamEnvelopeExtmsgOutbound, TypedEventStreamEnvelopeExtmsgUnbound, TypedEventStreamEnvelopeMailArchived, TypedEventStreamEnvelopeMailDeleted, TypedEventStreamEnvelopeMailMarkedRead, TypedEventStreamEnvelopeMailMarkedUnread, TypedEventStreamEnvelopeMailRead, TypedEventStreamEnvelopeMailReplied, TypedEventStreamEnvelopeMailSent, TypedEventStreamEnvelopeOrderCompleted, TypedEventStreamEnvelopeOrderFailed, TypedEventStreamEnvelopeOrderFired, TypedEventStreamEnvelopeProviderSwapped, TypedEventStreamEnvelopeSessionCrashed, TypedEventStreamEnvelopeSessionDraining, TypedEventStreamEnvelopeSessionIdleKilled, TypedEventStreamEnvelopeSessionQuarantined, TypedEventStreamEnvelopeSessionStopped, TypedEventStreamEnvelopeSessionSuspended, TypedEventStreamEnvelopeSessionUndrained, TypedEventStreamEnvelopeSessionUpdated, TypedEventStreamEnvelopeSessionWoke, TypedEventStreamEnvelopeWorkerOperation, TypedTaggedEventStreamEnvelope, TypedTaggedEventStreamEnvelopeBeadClosed, TypedTaggedEventStreamEnvelopeBeadCreated, TypedTaggedEventStreamEnvelopeBeadUpdated, TypedTaggedEventStreamEnvelopeCityCreated, TypedTaggedEventStreamEnvelopeCityInitFailed, TypedTaggedEventStreamEnvelopeCityReady, TypedTaggedEventStreamEnvelopeCityResumed, TypedTaggedEventStreamEnvelopeCitySuspended, TypedTaggedEventStreamEnvelopeCityUnregistered, TypedTaggedEventStreamEnvelopeCityUnregisterFailed, TypedTaggedEventStreamEnvelopeCityUnregisterRequested, TypedTaggedEventStreamEnvelopeControllerStarted, TypedTaggedEventStreamEnvelopeControllerStopped, TypedTaggedEventStreamEnvelopeConvoyClosed, TypedTaggedEventStreamEnvelopeConvoyCreated, TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded, TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved, TypedTaggedEventStreamEnvelopeExtmsgBound, TypedTaggedEventStreamEnvelopeExtmsgGroupCreated, TypedTaggedEventStreamEnvelopeExtmsgInbound, TypedTaggedEventStreamEnvelopeExtmsgOutbound, TypedTaggedEventStreamEnvelopeExtmsgUnbound, TypedTaggedEventStreamEnvelopeMailArchived, TypedTaggedEventStreamEnvelopeMailDeleted, TypedTaggedEventStreamEnvelopeMailMarkedRead, TypedTaggedEventStreamEnvelopeMailMarkedUnread, TypedTaggedEventStreamEnvelopeMailRead, TypedTaggedEventStreamEnvelopeMailReplied, TypedTaggedEventStreamEnvelopeMailSent, TypedTaggedEventStreamEnvelopeOrderCompleted, TypedTaggedEventStreamEnvelopeOrderFailed, TypedTaggedEventStreamEnvelopeOrderFired, TypedTaggedEventStreamEnvelopeProviderSwapped, TypedTaggedEventStreamEnvelopeSessionCrashed, TypedTaggedEventStreamEnvelopeSessionDraining, TypedTaggedEventStreamEnvelopeSessionIdleKilled, TypedTaggedEventStreamEnvelopeSessionQuarantined, TypedTaggedEventStreamEnvelopeSessionStopped, TypedTaggedEventStreamEnvelopeSessionSuspended, TypedTaggedEventStreamEnvelopeSessionUndrained, TypedTaggedEventStreamEnvelopeSessionUpdated, TypedTaggedEventStreamEnvelopeSessionWoke, TypedTaggedEventStreamEnvelopeWorkerOperation, UnboundEventPayload, WireEvent, WireTaggedEvent, WorkerOperationEventPayload, WorkflowAttemptSummary, WorkflowBeadResponse, WorkflowDeleteResponse, WorkflowDepResponse, WorkflowEventProjection, WorkflowSnapshotResponse, WorkspaceResponse } from './types.gen'; diff --git a/cmd/gc/dashboard/web/src/generated/schema.d.ts b/cmd/gc/dashboard/web/src/generated/schema.d.ts new file mode 100644 index 0000000000..9adf65ac86 --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/schema.d.ts @@ -0,0 +1,11535 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/health": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get health */ + get: operations["get-health"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/cities": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 cities */ + get: operations["get-v0-cities"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city */ + post: operations["post-v0-city"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name */ + get: operations["get-v0-city-by-city-name"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** Patch v0 city by city name */ + patch: operations["patch-v0-city-by-city-name"]; + trace?: never; + }; + "/v0/city/{cityName}/agent/{base}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name agent by base */ + get: operations["get-v0-city-by-city-name-agent-by-base"]; + put?: never; + post?: never; + /** Delete v0 city by city name agent by base */ + delete: operations["delete-v0-city-by-city-name-agent-by-base"]; + options?: never; + head?: never; + /** Patch v0 city by city name agent by base */ + patch: operations["patch-v0-city-by-city-name-agent-by-base"]; + trace?: never; + }; + "/v0/city/{cityName}/agent/{base}/output": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name agent by base output */ + get: operations["get-v0-city-by-city-name-agent-by-base-output"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/agent/{base}/output/stream": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Stream agent output in real time + * @description Server-Sent Events stream of agent output (session log tail or tmux pane polling). + */ + get: operations["stream-agent-output"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/agent/{base}/{action}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name agent by base by action */ + post: operations["post-v0-city-by-city-name-agent-by-base-by-action"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/agent/{dir}/{base}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name agent by dir by base */ + get: operations["get-v0-city-by-city-name-agent-by-dir-by-base"]; + put?: never; + post?: never; + /** Delete v0 city by city name agent by dir by base */ + delete: operations["delete-v0-city-by-city-name-agent-by-dir-by-base"]; + options?: never; + head?: never; + /** Patch v0 city by city name agent by dir by base */ + patch: operations["patch-v0-city-by-city-name-agent-by-dir-by-base"]; + trace?: never; + }; + "/v0/city/{cityName}/agent/{dir}/{base}/output": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name agent by dir by base output */ + get: operations["get-v0-city-by-city-name-agent-by-dir-by-base-output"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/agent/{dir}/{base}/output/stream": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Stream agent output in real time (qualified name) + * @description Server-Sent Events stream of agent output for qualified (rig-prefixed) agent names. + */ + get: operations["stream-agent-output-qualified"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/agent/{dir}/{base}/{action}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name agent by dir by base by action */ + post: operations["post-v0-city-by-city-name-agent-by-dir-by-base-by-action"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/agents": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name agents */ + get: operations["get-v0-city-by-city-name-agents"]; + put?: never; + /** Create an agent */ + post: operations["create-agent"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/bead/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name bead by ID */ + get: operations["get-v0-city-by-city-name-bead-by-id"]; + put?: never; + post?: never; + /** Delete v0 city by city name bead by ID */ + delete: operations["delete-v0-city-by-city-name-bead-by-id"]; + options?: never; + head?: never; + /** Patch v0 city by city name bead by ID */ + patch: operations["patch-v0-city-by-city-name-bead-by-id"]; + trace?: never; + }; + "/v0/city/{cityName}/bead/{id}/assign": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name bead by ID assign */ + post: operations["post-v0-city-by-city-name-bead-by-id-assign"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/bead/{id}/close": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name bead by ID close */ + post: operations["post-v0-city-by-city-name-bead-by-id-close"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/bead/{id}/deps": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name bead by ID deps */ + get: operations["get-v0-city-by-city-name-bead-by-id-deps"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/bead/{id}/reopen": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name bead by ID reopen */ + post: operations["post-v0-city-by-city-name-bead-by-id-reopen"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/bead/{id}/update": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name bead by ID update */ + post: operations["post-v0-city-by-city-name-bead-by-id-update"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/beads": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name beads */ + get: operations["get-v0-city-by-city-name-beads"]; + put?: never; + /** Create a bead */ + post: operations["create-bead"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/beads/graph/{rootID}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name beads graph by root ID */ + get: operations["get-v0-city-by-city-name-beads-graph-by-root-id"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/beads/ready": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name beads ready */ + get: operations["get-v0-city-by-city-name-beads-ready"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/config": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name config */ + get: operations["get-v0-city-by-city-name-config"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/config/explain": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name config explain */ + get: operations["get-v0-city-by-city-name-config-explain"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/config/validate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name config validate */ + get: operations["get-v0-city-by-city-name-config-validate"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/convoy/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name convoy by ID */ + get: operations["get-v0-city-by-city-name-convoy-by-id"]; + put?: never; + post?: never; + /** Delete v0 city by city name convoy by ID */ + delete: operations["delete-v0-city-by-city-name-convoy-by-id"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/convoy/{id}/add": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name convoy by ID add */ + post: operations["post-v0-city-by-city-name-convoy-by-id-add"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/convoy/{id}/check": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name convoy by ID check */ + get: operations["get-v0-city-by-city-name-convoy-by-id-check"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/convoy/{id}/close": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name convoy by ID close */ + post: operations["post-v0-city-by-city-name-convoy-by-id-close"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/convoy/{id}/remove": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name convoy by ID remove */ + post: operations["post-v0-city-by-city-name-convoy-by-id-remove"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/convoys": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name convoys */ + get: operations["get-v0-city-by-city-name-convoys"]; + put?: never; + /** Create a convoy */ + post: operations["create-convoy"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/events": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name events */ + get: operations["get-v0-city-by-city-name-events"]; + put?: never; + /** Emit an event */ + post: operations["emit-event"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/events/stream": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Stream city events in real time + * @description Server-Sent Events stream of city events with optional workflow projections. Supports reconnection via Last-Event-ID header or after_seq query param. + */ + get: operations["stream-events"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/extmsg/adapters": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name extmsg adapters */ + get: operations["get-v0-city-by-city-name-extmsg-adapters"]; + put?: never; + /** Register an external messaging adapter */ + post: operations["register-extmsg-adapter"]; + /** Delete v0 city by city name extmsg adapters */ + delete: operations["delete-v0-city-by-city-name-extmsg-adapters"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/extmsg/bind": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name extmsg bind */ + post: operations["post-v0-city-by-city-name-extmsg-bind"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/extmsg/bindings": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name extmsg bindings */ + get: operations["get-v0-city-by-city-name-extmsg-bindings"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/extmsg/groups": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name extmsg groups */ + get: operations["get-v0-city-by-city-name-extmsg-groups"]; + put?: never; + /** Ensure an external messaging group exists */ + post: operations["ensure-extmsg-group"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/extmsg/inbound": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name extmsg inbound */ + post: operations["post-v0-city-by-city-name-extmsg-inbound"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/extmsg/outbound": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name extmsg outbound */ + post: operations["post-v0-city-by-city-name-extmsg-outbound"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/extmsg/participants": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name extmsg participants */ + post: operations["post-v0-city-by-city-name-extmsg-participants"]; + /** Delete v0 city by city name extmsg participants */ + delete: operations["delete-v0-city-by-city-name-extmsg-participants"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/extmsg/transcript": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name extmsg transcript */ + get: operations["get-v0-city-by-city-name-extmsg-transcript"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/extmsg/transcript/ack": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name extmsg transcript ack */ + post: operations["post-v0-city-by-city-name-extmsg-transcript-ack"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/extmsg/unbind": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name extmsg unbind */ + post: operations["post-v0-city-by-city-name-extmsg-unbind"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/formula/{name}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name formula by name */ + get: operations["get-v0-city-by-city-name-formula-by-name"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/formulas": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name formulas */ + get: operations["get-v0-city-by-city-name-formulas"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/formulas/feed": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name formulas feed */ + get: operations["get-v0-city-by-city-name-formulas-feed"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/formulas/{name}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name formulas by name */ + get: operations["get-v0-city-by-city-name-formulas-by-name"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/formulas/{name}/preview": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name formulas by name preview */ + post: operations["post-v0-city-by-city-name-formulas-by-name-preview"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/formulas/{name}/runs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name formulas by name runs */ + get: operations["get-v0-city-by-city-name-formulas-by-name-runs"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/health": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name health */ + get: operations["get-v0-city-by-city-name-health"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/mail": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name mail */ + get: operations["get-v0-city-by-city-name-mail"]; + put?: never; + /** Send a mail message */ + post: operations["send-mail"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/mail/count": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name mail count */ + get: operations["get-v0-city-by-city-name-mail-count"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/mail/thread/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name mail thread by ID */ + get: operations["get-v0-city-by-city-name-mail-thread-by-id"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/mail/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name mail by ID */ + get: operations["get-v0-city-by-city-name-mail-by-id"]; + put?: never; + post?: never; + /** Delete v0 city by city name mail by ID */ + delete: operations["delete-v0-city-by-city-name-mail-by-id"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/mail/{id}/archive": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name mail by ID archive */ + post: operations["post-v0-city-by-city-name-mail-by-id-archive"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/mail/{id}/mark-unread": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name mail by ID mark unread */ + post: operations["post-v0-city-by-city-name-mail-by-id-mark-unread"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/mail/{id}/read": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name mail by ID read */ + post: operations["post-v0-city-by-city-name-mail-by-id-read"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/mail/{id}/reply": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Reply to a mail message */ + post: operations["reply-mail"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/order/history/{bead_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name order history by bead ID */ + get: operations["get-v0-city-by-city-name-order-history-by-bead-id"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/order/{name}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name order by name */ + get: operations["get-v0-city-by-city-name-order-by-name"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/order/{name}/disable": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name order by name disable */ + post: operations["post-v0-city-by-city-name-order-by-name-disable"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/order/{name}/enable": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name order by name enable */ + post: operations["post-v0-city-by-city-name-order-by-name-enable"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/orders": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name orders */ + get: operations["get-v0-city-by-city-name-orders"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/orders/check": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name orders check */ + get: operations["get-v0-city-by-city-name-orders-check"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/orders/feed": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name orders feed */ + get: operations["get-v0-city-by-city-name-orders-feed"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/orders/history": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name orders history */ + get: operations["get-v0-city-by-city-name-orders-history"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/packs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name packs */ + get: operations["get-v0-city-by-city-name-packs"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/patches/agent/{base}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name patches agent by base */ + get: operations["get-v0-city-by-city-name-patches-agent-by-base"]; + put?: never; + post?: never; + /** Delete v0 city by city name patches agent by base */ + delete: operations["delete-v0-city-by-city-name-patches-agent-by-base"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/patches/agent/{dir}/{base}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name patches agent by dir by base */ + get: operations["get-v0-city-by-city-name-patches-agent-by-dir-by-base"]; + put?: never; + post?: never; + /** Delete v0 city by city name patches agent by dir by base */ + delete: operations["delete-v0-city-by-city-name-patches-agent-by-dir-by-base"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/patches/agents": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name patches agents */ + get: operations["get-v0-city-by-city-name-patches-agents"]; + /** Put v0 city by city name patches agents */ + put: operations["put-v0-city-by-city-name-patches-agents"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/patches/provider/{name}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name patches provider by name */ + get: operations["get-v0-city-by-city-name-patches-provider-by-name"]; + put?: never; + post?: never; + /** Delete v0 city by city name patches provider by name */ + delete: operations["delete-v0-city-by-city-name-patches-provider-by-name"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/patches/providers": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name patches providers */ + get: operations["get-v0-city-by-city-name-patches-providers"]; + /** Put v0 city by city name patches providers */ + put: operations["put-v0-city-by-city-name-patches-providers"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/patches/rig/{name}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name patches rig by name */ + get: operations["get-v0-city-by-city-name-patches-rig-by-name"]; + put?: never; + post?: never; + /** Delete v0 city by city name patches rig by name */ + delete: operations["delete-v0-city-by-city-name-patches-rig-by-name"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/patches/rigs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name patches rigs */ + get: operations["get-v0-city-by-city-name-patches-rigs"]; + /** Put v0 city by city name patches rigs */ + put: operations["put-v0-city-by-city-name-patches-rigs"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/provider-readiness": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name provider readiness */ + get: operations["get-v0-city-by-city-name-provider-readiness"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/provider/{name}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name provider by name */ + get: operations["get-v0-city-by-city-name-provider-by-name"]; + put?: never; + post?: never; + /** Delete v0 city by city name provider by name */ + delete: operations["delete-v0-city-by-city-name-provider-by-name"]; + options?: never; + head?: never; + /** Patch v0 city by city name provider by name */ + patch: operations["patch-v0-city-by-city-name-provider-by-name"]; + trace?: never; + }; + "/v0/city/{cityName}/providers": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name providers */ + get: operations["get-v0-city-by-city-name-providers"]; + put?: never; + /** Create a provider */ + post: operations["create-provider"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/providers/public": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name providers public */ + get: operations["get-v0-city-by-city-name-providers-public"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/readiness": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name readiness */ + get: operations["get-v0-city-by-city-name-readiness"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/rig/{name}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name rig by name */ + get: operations["get-v0-city-by-city-name-rig-by-name"]; + put?: never; + post?: never; + /** Delete v0 city by city name rig by name */ + delete: operations["delete-v0-city-by-city-name-rig-by-name"]; + options?: never; + head?: never; + /** Patch v0 city by city name rig by name */ + patch: operations["patch-v0-city-by-city-name-rig-by-name"]; + trace?: never; + }; + "/v0/city/{cityName}/rig/{name}/{action}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name rig by name by action */ + post: operations["post-v0-city-by-city-name-rig-by-name-by-action"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/rigs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name rigs */ + get: operations["get-v0-city-by-city-name-rigs"]; + put?: never; + /** Create a rig */ + post: operations["create-rig"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/service/{name}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name service by name */ + get: operations["get-v0-city-by-city-name-service-by-name"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/service/{name}/restart": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name service by name restart */ + post: operations["post-v0-city-by-city-name-service-by-name-restart"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/services": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name services */ + get: operations["get-v0-city-by-city-name-services"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name session by ID */ + get: operations["get-v0-city-by-city-name-session-by-id"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** Patch v0 city by city name session by ID */ + patch: operations["patch-v0-city-by-city-name-session-by-id"]; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/agents": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name session by ID agents */ + get: operations["get-v0-city-by-city-name-session-by-id-agents"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/agents/{agentId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name session by ID agents by agent ID */ + get: operations["get-v0-city-by-city-name-session-by-id-agents-by-agent-id"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/close": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name session by ID close */ + post: operations["post-v0-city-by-city-name-session-by-id-close"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/kill": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name session by ID kill */ + post: operations["post-v0-city-by-city-name-session-by-id-kill"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/messages": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Send a message to a session */ + post: operations["send-session-message"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/pending": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name session by ID pending */ + get: operations["get-v0-city-by-city-name-session-by-id-pending"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/rename": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name session by ID rename */ + post: operations["post-v0-city-by-city-name-session-by-id-rename"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/respond": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Respond to a pending interaction */ + post: operations["respond-session"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/stop": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name session by ID stop */ + post: operations["post-v0-city-by-city-name-session-by-id-stop"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/stream": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Stream session output in real time + * @description Server-Sent Events stream of session transcript updates. Streams turns (conversation format) or raw messages (JSONL format) based on the format query parameter. Emits activity and pending events for tool approval prompts. + */ + get: operations["stream-session"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/submit": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Submit a message to a session */ + post: operations["submit-session"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/suspend": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name session by ID suspend */ + post: operations["post-v0-city-by-city-name-session-by-id-suspend"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/transcript": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name session by ID transcript */ + get: operations["get-v0-city-by-city-name-session-by-id-transcript"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/session/{id}/wake": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name session by ID wake */ + post: operations["post-v0-city-by-city-name-session-by-id-wake"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/sessions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name sessions */ + get: operations["get-v0-city-by-city-name-sessions"]; + put?: never; + /** Create a session */ + post: operations["create-session"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/sling": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name sling */ + post: operations["post-v0-city-by-city-name-sling"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/status": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name status */ + get: operations["get-v0-city-by-city-name-status"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/unregister": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name unregister */ + post: operations["post-v0-city-by-city-name-unregister"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/city/{cityName}/workflow/{workflow_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 city by city name workflow by workflow ID */ + get: operations["get-v0-city-by-city-name-workflow-by-workflow-id"]; + put?: never; + post?: never; + /** Delete v0 city by city name workflow by workflow ID */ + delete: operations["delete-v0-city-by-city-name-workflow-by-workflow-id"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/events": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 events */ + get: operations["get-v0-events"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/events/stream": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Stream tagged events from all running cities. */ + get: operations["stream-supervisor-events"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/provider-readiness": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 provider readiness */ + get: operations["get-v0-provider-readiness"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/readiness": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get v0 readiness */ + get: operations["get-v0-readiness"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + AdapterCapabilities: { + /** Format: int64 */ + MaxMessageLength: number; + SupportsAttachments: boolean; + SupportsChildConversations: boolean; + }; + AdapterEventPayload: { + account_id: string; + provider: string; + }; + AgentCreateInputBody: { + /** @description Working directory (rig name). */ + dir?: string; + /** + * @description Agent name. + * @example deacon-1 + */ + name: string; + /** + * @description Provider name. + * @example claude + */ + provider: string; + /** @description Agent scope. */ + scope?: string; + }; + AgentCreatedOutputBody: { + /** @description Created agent name. */ + agent: string; + /** + * @description Operation result. + * @example created + */ + status: string; + }; + AgentMapping: { + agent_id: string; + parent_tool_use_id: string; + }; + AgentOutputResponse: { + agent: string; + format: string; + pagination?: components["schemas"]["PaginationInfo"]; + turns: components["schemas"]["OutputTurn"][] | null; + }; + AgentPatch: { + AppendFragments: string[] | null; + Attach: boolean | null; + DefaultSlingFormula: string | null; + DependsOn: string[] | null; + Dir: string; + Env: { + [key: string]: string; + }; + EnvRemove: string[] | null; + HooksInstalled: boolean | null; + IdleTimeout: string | null; + InjectAssignedSkills: boolean | null; + InjectFragments: string[] | null; + InjectFragmentsAppend: string[] | null; + InstallAgentHooks: string[] | null; + InstallAgentHooksAppend: string[] | null; + MCP: string[] | null; + MCPAppend: string[] | null; + /** Format: int64 */ + MaxActiveSessions: number | null; + /** Format: int64 */ + MinActiveSessions: number | null; + Name: string; + Nudge: string | null; + OptionDefaults: { + [key: string]: string; + }; + OverlayDir: string | null; + Pool: components["schemas"]["PoolOverride"]; + PreStart: string[] | null; + PreStartAppend: string[] | null; + PromptTemplate: string | null; + Provider: string | null; + ResumeCommand: string | null; + ScaleCheck: string | null; + Scope: string | null; + Session: string | null; + SessionLive: string[] | null; + SessionLiveAppend: string[] | null; + SessionSetup: string[] | null; + SessionSetupAppend: string[] | null; + SessionSetupScript: string | null; + Skills: string[] | null; + SkillsAppend: string[] | null; + SleepAfterIdle: string | null; + StartCommand: string | null; + Suspended: boolean | null; + WakeMode: string | null; + WorkDir: string | null; + }; + AgentPatchSetInputBody: { + /** @description Agent directory scope. */ + dir?: string; + /** @description Override environment variables. */ + env?: { + [key: string]: string; + }; + /** @description Agent name. */ + name?: string; + /** @description Override agent scope. */ + scope?: string; + /** @description Override suspended state. */ + suspended?: boolean; + /** @description Override session working directory. */ + work_dir?: string; + }; + AgentResponse: { + active_bead?: string; + activity?: string; + available: boolean; + /** Format: int64 */ + context_pct?: number; + /** Format: int64 */ + context_window?: number; + description?: string; + display_name?: string; + last_output?: string; + model?: string; + name: string; + pool?: string; + provider?: string; + rig?: string; + running: boolean; + session?: components["schemas"]["SessionInfo"]; + state: string; + suspended: boolean; + unavailable_reason?: string; + }; + AgentUpdateInputBody: { + /** @description Provider name. */ + provider?: string; + /** @description Agent scope. */ + scope?: string; + /** @description Whether agent is suspended. */ + suspended?: boolean; + }; + AgentUpdateQualifiedInputBody: { + /** @description Provider name. */ + provider?: string; + /** @description Agent scope. */ + scope?: string; + /** @description Whether agent is suspended. */ + suspended?: boolean; + }; + AnnotatedAgentResponse: { + dir?: string; + is_pool?: boolean; + name: string; + /** @description Agent origin: inline or pack-derived. */ + origin: string; + provider?: string; + scope?: string; + suspended: boolean; + }; + AnnotatedProviderResponse: { + acp_args?: string[]; + acp_command?: string; + args?: string[] | null; + command?: string; + display_name?: string; + env?: { + [key: string]: string; + }; + /** @description Provider origin: builtin, city, or builtin+city. */ + origin: string; + prompt_flag?: string; + prompt_mode?: string; + /** Format: int64 */ + ready_delay_ms?: number; + }; + Bead: { + assignee?: string; + /** Format: date-time */ + created_at: string; + dependencies?: components["schemas"]["Dep"][] | null; + description?: string; + from?: string; + id: string; + issue_type: string; + labels?: string[] | null; + metadata?: { + [key: string]: string; + }; + needs?: string[] | null; + parent?: string; + /** Format: int64 */ + priority?: number; + ref?: string; + status: string; + title: string; + }; + BeadAssignInputBody: { + /** @description Assignee name. */ + assignee?: string; + }; + BeadCreateInputBody: { + /** @description Assigned agent. */ + assignee?: string; + /** @description Bead description. */ + description?: string; + /** @description Bead labels. */ + labels?: string[] | null; + /** @description Metadata key-value pairs to set at create time. */ + metadata?: { + [key: string]: string; + }; + /** @description Parent bead ID. */ + parent?: string; + /** + * Format: int64 + * @description Bead priority. + */ + priority?: number; + /** @description Rig name. */ + rig?: string; + /** @description Bead title. */ + title: string; + /** @description Bead type. */ + type?: string; + }; + BeadDepsResponse: { + children: components["schemas"]["Bead"][] | null; + }; + BeadEventPayload: { + bead: components["schemas"]["Bead"]; + }; + BeadGraphResponse: { + beads: components["schemas"]["Bead"][] | null; + deps: components["schemas"]["WorkflowDepResponse"][] | null; + root: components["schemas"]["Bead"]; + }; + BeadUpdateBody: { + /** @description Assigned agent. */ + assignee?: string; + /** @description Bead description. */ + description?: string; + /** @description Bead labels. */ + labels?: string[] | null; + /** @description Metadata key-value pairs to set. */ + metadata?: { + [key: string]: string; + }; + /** @description Parent bead ID. Use null or an empty string to clear. */ + parent?: string | null; + /** + * Format: int64 + * @description Bead priority. + */ + priority?: number; + /** @description Labels to remove. */ + remove_labels?: string[] | null; + /** @description Bead status. */ + status?: string; + /** @description Bead title. */ + title?: string; + /** @description Bead type. */ + type?: string; + }; + /** + * @description Lifecycle state of a session binding. + * @enum {string} + */ + BindingStatus: "active" | "ended"; + BoundEventPayload: { + conversation_id: string; + provider: string; + session_id: string; + }; + CityCreateRequest: { + /** + * @description Optional bootstrap profile. + * @enum {string} + */ + bootstrap_profile?: "k8s-cell" | "kubernetes" | "kubernetes-cell" | "single-host-compat"; + /** @description Directory to create the city in. Absolute or relative to $HOME. */ + dir: string; + /** @description Provider name for the city's default session template. */ + provider: string; + }; + CityCreateResponse: { + /** @description Resolved city name as persisted in city.toml. Use this to filter the event stream for completion. */ + name: string; + /** @description True when scaffolding + registration succeeded. Does not imply the city is ready yet; watch /v0/events/stream for city.ready. */ + ok: boolean; + /** @description Resolved absolute path of the created city directory. */ + path: string; + }; + CityGetResponse: { + /** Format: int64 */ + agent_count: number; + name: string; + path: string; + provider?: string; + /** Format: int64 */ + rig_count: number; + session_template?: string; + suspended: boolean; + /** Format: int64 */ + uptime_sec: number; + version?: string; + }; + CityInfo: { + error?: string; + name: string; + path: string; + phases_completed?: string[] | null; + running: boolean; + status?: string; + }; + CityLifecyclePayload: { + error?: string; + name: string; + path: string; + phases_completed?: string[] | null; + }; + CityPatchInputBody: { + /** @description Whether the city is suspended. */ + suspended?: boolean; + }; + CityUnregisterResponse: { + /** @description Resolved registry name. Filter the event stream by this to observe completion. */ + name: string; + /** @description True when the registry entry was removed and the supervisor was signaled. Does not imply the city's controller has stopped yet; watch /v0/events/stream for city.unregistered. */ + ok: boolean; + /** @description Resolved absolute city directory. The directory itself is not modified; unregister only affects the supervisor's registry. */ + path: string; + }; + ConfigAgentResponse: { + dir?: string; + is_pool?: boolean; + name: string; + provider?: string; + scope?: string; + suspended: boolean; + }; + ConfigExplainPatches: { + /** Format: int64 */ + agents: number; + /** Format: int64 */ + providers: number; + /** Format: int64 */ + rigs: number; + }; + ConfigExplainResponse: { + agents: components["schemas"]["AnnotatedAgentResponse"][] | null; + patches: components["schemas"]["ConfigExplainPatches"]; + providers: { + [key: string]: components["schemas"]["AnnotatedProviderResponse"]; + }; + }; + ConfigPatchesResponse: { + /** Format: int64 */ + agent_count: number; + /** Format: int64 */ + provider_count: number; + /** Format: int64 */ + rig_count: number; + }; + ConfigResponse: { + agents: components["schemas"]["ConfigAgentResponse"][] | null; + patches?: components["schemas"]["ConfigPatchesResponse"]; + providers?: { + [key: string]: components["schemas"]["ProviderSpecJSON"]; + }; + rigs: components["schemas"]["ConfigRigResponse"][] | null; + workspace: components["schemas"]["WorkspaceResponse"]; + }; + ConfigRigResponse: { + name: string; + path: string; + prefix?: string; + suspended: boolean; + }; + ConfigValidateOutputBody: { + /** @description Validation errors. */ + errors: string[] | null; + /** @description Whether the configuration is valid. */ + valid: boolean; + /** @description Validation warnings. */ + warnings: string[] | null; + }; + ConversationGroupParticipant: { + GroupID: string; + Handle: string; + ID: string; + Metadata: { + [key: string]: string; + }; + Public: boolean; + SessionID: string; + }; + ConversationGroupRecord: { + DefaultHandle: string; + FanoutPolicy: components["schemas"]["FanoutPolicy"]; + ID: string; + LastAddressedHandle: string; + Metadata: { + [key: string]: string; + }; + Mode: string; + RootConversation: components["schemas"]["ConversationRef"]; + /** Format: int64 */ + SchemaVersion: number; + }; + /** + * @description Shape of a conversation. + * @enum {string} + */ + ConversationKind: "dm" | "room" | "thread"; + ConversationRef: { + account_id: string; + conversation_id: string; + kind: components["schemas"]["ConversationKind"]; + parent_conversation_id?: string; + provider: string; + scope_id: string; + }; + ConversationTranscriptRecord: { + Actor: components["schemas"]["ExternalActor"]; + Attachments: components["schemas"]["ExternalAttachment"][] | null; + Conversation: components["schemas"]["ConversationRef"]; + /** Format: date-time */ + CreatedAt: string; + ExplicitTarget: string; + ID: string; + Kind: components["schemas"]["TranscriptMessageKind"]; + Metadata: { + [key: string]: string; + }; + Provenance: components["schemas"]["TranscriptProvenance"]; + ProviderMessageID: string; + ReplyToMessageID: string; + /** Format: int64 */ + SchemaVersion: number; + /** Format: int64 */ + Sequence: number; + SourceSessionID: string; + Text: string; + }; + ConvoyAddInputBody: { + /** @description Bead IDs to add. */ + items?: string[] | null; + }; + ConvoyCheckResponse: { + /** + * Format: int64 + * @description Closed child bead count. + */ + closed: number; + /** @description True when all child beads are closed and total > 0. */ + complete: boolean; + /** @description Convoy ID. */ + convoy_id: string; + /** + * Format: int64 + * @description Total child bead count. + */ + total: number; + }; + ConvoyCreateInputBody: { + /** @description Bead IDs to include. */ + items?: string[] | null; + /** @description Rig name. */ + rig?: string; + /** @description Convoy title. */ + title: string; + }; + ConvoyGetResponse: { + /** @description Direct child beads (non-workflow case). */ + children?: components["schemas"]["Bead"][] | null; + /** @description Simple convoy bead (non-workflow case). */ + convoy?: components["schemas"]["Bead"]; + /** @description Child bead progress (non-workflow case). */ + progress?: components["schemas"]["ConvoyProgress"]; + }; + ConvoyProgress: { + /** + * Format: int64 + * @description Closed child bead count. + */ + closed: number; + /** + * Format: int64 + * @description Total child bead count. + */ + total: number; + }; + ConvoyRemoveInputBody: { + /** @description Bead IDs to remove. */ + items?: string[] | null; + }; + DeliveryContextRecord: { + /** Format: int64 */ + BindingGeneration: number; + Conversation: components["schemas"]["ConversationRef"]; + ID: string; + LastMessageID: string; + /** Format: date-time */ + LastPublishedAt: string; + Metadata: { + [key: string]: string; + }; + /** Format: int64 */ + SchemaVersion: number; + SessionID: string; + SourceSessionID: string; + }; + Dep: { + depends_on_id: string; + issue_id: string; + type: string; + }; + ErrorDetail: { + /** @description Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id' */ + location?: string; + /** @description Error message text */ + message?: string; + /** @description The value at the given location */ + value?: unknown; + }; + ErrorModel: { + /** + * @description A human-readable explanation specific to this occurrence of the problem. + * @example Property foo is required but is missing. + */ + detail?: string; + /** @description Optional list of individual error details */ + errors?: components["schemas"]["ErrorDetail"][] | null; + /** + * Format: uri + * @description A URI reference that identifies the specific occurrence of the problem. + * @example https://example.com/error-log/abc123 + */ + instance?: string; + /** + * Format: int64 + * @description HTTP status code + * @example 400 + */ + status?: number; + /** + * @description A short, human-readable summary of the problem type. This value should not change between occurrences of the error. + * @example Bad Request + */ + title?: string; + /** + * Format: uri + * @description A URI reference to human-readable documentation for the error. + * @default about:blank + * @example https://example.com/errors/example + * @example urn:gascity:error:sling-missing-bead + * @example urn:gascity:error:sling-cross-rig + */ + type: string; + }; + EventEmitOutputBody: { + /** + * @description Operation result. + * @example recorded + */ + status: string; + }; + EventEmitRequest: { + /** @description Actor that produced the event. */ + actor: string; + /** @description Event message. */ + message?: string; + /** @description Event subject. */ + subject?: string; + /** @description Event type. */ + type: string; + }; + EventPayload: components["schemas"]["AdapterEventPayload"] | components["schemas"]["BeadEventPayload"] | components["schemas"]["BoundEventPayload"] | components["schemas"]["CityLifecyclePayload"] | components["schemas"]["GroupCreatedEventPayload"] | components["schemas"]["InboundEventPayload"] | components["schemas"]["MailEventPayload"] | components["schemas"]["NoPayload"] | components["schemas"]["OutboundEventPayload"] | components["schemas"]["UnboundEventPayload"] | components["schemas"]["WorkerOperationEventPayload"]; + EventStreamEnvelope: { + actor: string; + message?: string; + payload?: components["schemas"]["EventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + type: string; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + ExtMsgAdapterRegisterInputBody: { + /** @description Account ID. */ + account_id: string; + /** @description Callback URL for outbound messages. */ + callback_url?: string; + /** @description Adapter capabilities. */ + capabilities?: components["schemas"]["AdapterCapabilities"]; + /** @description Adapter display name. */ + name?: string; + /** @description Provider name. */ + provider: string; + }; + ExtMsgAdapterRegisterOutputBody: { + /** @description Account ID. */ + account_id: string; + /** @description Adapter name. */ + name: string; + /** @description Provider name. */ + provider: string; + /** + * @description Operation result. + * @example registered + */ + status: string; + }; + ExtMsgAdapterUnregisterInputBody: { + /** @description Account ID. */ + account_id: string; + /** @description Provider name. */ + provider: string; + }; + ExtMsgBindInputBody: { + /** @description Conversation to bind. */ + conversation?: components["schemas"]["ConversationRef"]; + /** @description Optional binding metadata. */ + metadata?: { + [key: string]: string; + }; + /** @description Session ID to bind. */ + session_id: string; + }; + ExtMsgGroupEnsureInputBody: { + /** @description Default handle for the group. */ + default_handle?: string; + /** @description Group metadata. */ + metadata?: { + [key: string]: string; + }; + /** @description Group mode (launcher, etc.). */ + mode?: string; + /** @description Root conversation reference. */ + root_conversation?: components["schemas"]["ConversationRef"]; + }; + ExtMsgInboundInputBody: { + /** @description Account ID for raw payloads (required when message is absent). */ + account_id?: string; + /** @description Pre-normalized inbound message. */ + message?: components["schemas"]["ExternalInboundMessage"]; + /** @description Raw payload bytes. */ + payload?: string; + /** @description Provider name for raw payloads (required when message is absent). */ + provider?: string; + }; + ExtMsgOutboundInputBody: { + /** @description Target conversation. */ + conversation?: components["schemas"]["ConversationRef"]; + /** @description Idempotency key. */ + idempotency_key?: string; + /** @description Message ID to reply to. */ + reply_to_message_id?: string; + /** @description Session ID. */ + session_id: string; + /** @description Message text. */ + text?: string; + }; + ExtMsgParticipantRemoveInputBody: { + /** @description Group ID. */ + group_id: string; + /** @description Participant handle. */ + handle: string; + }; + ExtMsgParticipantUpsertInputBody: { + /** @description Group ID. */ + group_id: string; + /** @description Participant handle. */ + handle: string; + /** @description Participant metadata. */ + metadata?: { + [key: string]: string; + }; + /** @description Whether participant is public. */ + public?: boolean; + /** @description Session ID. */ + session_id: string; + }; + ExtMsgTranscriptAckInputBody: { + /** @description Conversation to acknowledge. */ + conversation?: components["schemas"]["ConversationRef"]; + /** + * Format: int64 + * @description Sequence number to acknowledge up to. + */ + sequence?: number; + /** @description Session ID. */ + session_id: string; + }; + ExtMsgUnbindBody: { + /** @description Bindings that were removed. */ + unbound: components["schemas"]["SessionBindingRecord"][] | null; + }; + ExtMsgUnbindInputBody: { + /** @description Conversation to unbind (nil = all). */ + conversation?: components["schemas"]["ConversationRef"]; + /** @description Session ID to unbind. */ + session_id: string; + }; + ExternalActor: { + display_name: string; + id: string; + is_bot: boolean; + }; + ExternalAttachment: { + mime_type: string; + provider_id: string; + url: string; + }; + ExternalInboundMessage: { + actor: components["schemas"]["ExternalActor"]; + attachments?: components["schemas"]["ExternalAttachment"][] | null; + conversation: components["schemas"]["ConversationRef"]; + dedup_key?: string; + explicit_target?: string; + provider_message_id: string; + /** Format: date-time */ + received_at: string; + reply_to_message_id?: string; + text: string; + }; + ExtmsgAdapterInfo: { + /** @description Adapter account ID. */ + account_id: string; + /** @description Adapter display name. */ + name: string; + /** @description Adapter provider key. */ + provider: string; + }; + FanoutPolicy: { + AllowUntargetedPublication: boolean; + Enabled: boolean; + /** Format: int64 */ + MaxPeerTriggeredPublishes: number; + /** Format: int64 */ + MaxTotalPeerDeliveries: number; + }; + FormulaDetailResponse: { + deps: components["schemas"]["FormulaPreviewEdgeResponse"][] | null; + description: string; + name: string; + preview: components["schemas"]["FormulaPreviewResponse"]; + steps: components["schemas"]["FormulaStepResponse"][] | null; + var_defs: components["schemas"]["FormulaVarDefResponse"][] | null; + version: string; + }; + FormulaFeedBody: { + items: components["schemas"]["MonitorFeedItemResponse"][] | null; + partial: boolean; + partial_errors?: string[] | null; + }; + FormulaListBody: { + /** @description Formula summaries. */ + items: components["schemas"]["FormulaSummaryResponse"][] | null; + /** @description Whether the list is partial. */ + partial: boolean; + }; + FormulaPreviewBody: { + /** @description Scope kind (city or rig). */ + scope_kind?: string; + /** @description Scope reference. */ + scope_ref?: string; + /** @description Target agent for preview compilation. */ + target: string; + /** @description Variable name-to-value overrides applied to the compiled preview. */ + vars?: { + [key: string]: string; + }; + }; + FormulaPreviewEdgeResponse: { + from: string; + kind?: string; + to: string; + }; + FormulaPreviewNodeResponse: { + id: string; + kind: string; + scope_ref?: string; + title: string; + }; + FormulaPreviewResponse: { + edges: components["schemas"]["FormulaPreviewEdgeResponse"][] | null; + nodes: components["schemas"]["FormulaPreviewNodeResponse"][] | null; + }; + FormulaRecentRunResponse: { + started_at: string; + status: string; + target: string; + updated_at: string; + workflow_id: string; + }; + FormulaRunsResponse: { + formula: string; + partial: boolean; + partial_errors?: string[] | null; + recent_runs: components["schemas"]["FormulaRecentRunResponse"][] | null; + /** Format: int64 */ + run_count: number; + }; + FormulaStepResponse: { + assignee?: string; + id: string; + kind: string; + labels?: string[] | null; + metadata?: { + [key: string]: string; + }; + title: string; + type?: string; + }; + FormulaSummaryResponse: { + description: string; + name: string; + recent_runs: components["schemas"]["FormulaRecentRunResponse"][] | null; + /** Format: int64 */ + run_count: number; + var_defs: components["schemas"]["FormulaVarDefResponse"][] | null; + version: string; + }; + FormulaVarDefResponse: { + default?: unknown; + description?: string; + enum?: string[] | null; + name: string; + pattern?: string; + required?: boolean; + type: string; + }; + GitStatus: { + /** Format: int64 */ + ahead: number; + /** Format: int64 */ + behind: number; + branch: string; + /** Format: int64 */ + changed_files: number; + clean: boolean; + }; + GroupCreatedEventPayload: { + conversation_id: string; + mode: string; + provider: string; + }; + GroupRouteDecision: { + Match: string; + TargetSessionID: string; + UpdateCursor: boolean; + }; + HealthOutputBody: { + /** @description City name. */ + city?: string; + /** + * @description Health status. + * @example ok + */ + status: string; + /** + * Format: int64 + * @description Server uptime in seconds. + */ + uptime_sec: number; + /** @description Server version. */ + version?: string; + }; + HeartbeatEvent: { + /** @description ISO 8601 timestamp when the heartbeat was sent. */ + timestamp: string; + }; + InboundEventPayload: { + actor: string; + conversation_id: string; + provider: string; + target_session: string; + }; + InboundResult: { + Binding: components["schemas"]["SessionBindingRecord"]; + GroupRoute: components["schemas"]["GroupRouteDecision"]; + Message: components["schemas"]["ExternalInboundMessage"]; + TargetSessionID: string; + TranscriptEntry: components["schemas"]["ConversationTranscriptRecord"]; + }; + ListBodyAgentPatch: { + /** @description The list of items. */ + items: components["schemas"]["AgentPatch"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + ListBodyAgentResponse: { + /** @description The list of items. */ + items: components["schemas"]["AgentResponse"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + ListBodyBead: { + /** @description The list of items. */ + items: components["schemas"]["Bead"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + ListBodyConversationTranscriptRecord: { + /** @description The list of items. */ + items: components["schemas"]["ConversationTranscriptRecord"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + ListBodyExtmsgAdapterInfo: { + /** @description The list of items. */ + items: components["schemas"]["ExtmsgAdapterInfo"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + ListBodyProviderPatch: { + /** @description The list of items. */ + items: components["schemas"]["ProviderPatch"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + ListBodyProviderResponse: { + /** @description The list of items. */ + items: components["schemas"]["ProviderResponse"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + ListBodyRigPatch: { + /** @description The list of items. */ + items: components["schemas"]["RigPatch"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + ListBodyRigResponse: { + /** @description The list of items. */ + items: components["schemas"]["RigResponse"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + ListBodySessionBindingRecord: { + /** @description The list of items. */ + items: components["schemas"]["SessionBindingRecord"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + ListBodySessionResponse: { + /** @description The list of items. */ + items: components["schemas"]["SessionResponse"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + ListBodyStatus: { + /** @description The list of items. */ + items: components["schemas"]["Status"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + ListBodyWireEvent: { + /** @description The list of items. */ + items: components["schemas"]["WireEvent"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more backends failed and the list is incomplete. */ + partial?: boolean; + /** @description Human-readable errors from backends that failed during aggregation. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of items matching the query. + */ + total: number; + }; + LogicalNode: Record; + MailCountOutputBody: { + /** @description True when one or more rig providers failed and the counts are not authoritative. */ + partial?: boolean; + /** @description Per-provider errors when partial is true. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total message count. + */ + total: number; + /** + * Format: int64 + * @description Unread message count. + */ + unread: number; + }; + MailEventPayload: { + message?: components["schemas"]["Message"]; + rig: string; + }; + MailListBody: { + /** @description The list of messages. */ + items: components["schemas"]["Message"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** @description True when one or more rig providers failed and the list is not authoritative. */ + partial?: boolean; + /** @description Per-provider errors when partial is true. */ + partial_errors?: string[] | null; + /** + * Format: int64 + * @description Total number of messages matching the query. + */ + total: number; + }; + MailReplyInputBody: { + /** @description Reply body. */ + body?: string; + /** @description Sender name. */ + from?: string; + /** @description Reply subject. */ + subject?: string; + }; + MailSendInputBody: { + /** @description Message body. */ + body?: string; + /** @description Sender name. */ + from?: string; + /** @description Rig name. */ + rig?: string; + /** @description Message subject. */ + subject: string; + /** @description Recipient name. */ + to: string; + }; + Message: { + body: string; + cc?: string[] | null; + /** Format: date-time */ + created_at: string; + from: string; + id: string; + /** Format: int64 */ + priority?: number; + read: boolean; + reply_to?: string; + rig?: string; + subject: string; + thread_id?: string; + to: string; + }; + MonitorFeedItemResponse: { + attached_bead_id?: string; + bead_id?: string; + detail_available?: boolean; + id: string; + logical_bead_id?: string; + root_bead_id?: string; + root_store_ref?: string; + run_detail_available?: boolean; + scope_kind: string; + scope_ref: string; + started_at: string; + status: string; + store_ref?: string; + target: string; + title: string; + type: string; + updated_at: string; + workflow_id?: string; + }; + NoPayload: Record; + OKResponseBody: { + /** + * @description Operation result. + * @example ok + */ + status: string; + }; + OKWithIDResponseBody: { + /** @description Resource ID. */ + id?: string; + /** + * @description Operation result. + * @example ok + */ + status: string; + }; + OptionChoiceDTO: { + label: string; + value: string; + }; + OrderCheckListBody: { + /** @description Order trigger evaluations. */ + checks: components["schemas"]["OrderCheckResponse"][] | null; + }; + OrderCheckResponse: { + due: boolean; + last_run?: string; + last_run_outcome?: string; + name: string; + reason: string; + rig?: string; + scoped_name: string; + }; + OrderHistoryDetailResponse: { + bead_id: string; + created_at: string; + labels: string[] | null; + output: string; + store_ref: string; + }; + OrderHistoryEntry: { + bead_id: string; + capture_output: boolean; + created_at: string; + duration_ms?: string; + error?: string; + exit_code?: string; + has_output: boolean; + labels: string[] | null; + name: string; + rig?: string; + scoped_name: string; + signal?: string; + store_ref: string; + wisp_root_id?: string; + }; + OrderHistoryListBody: { + /** @description Order history entries. */ + entries: components["schemas"]["OrderHistoryEntry"][] | null; + }; + OrderListBody: { + /** @description Registered orders. */ + orders: components["schemas"]["OrderResponse"][] | null; + }; + OrderResponse: { + capture_output: boolean; + check?: string; + description?: string; + enabled: boolean; + exec?: string; + formula?: string; + /** @deprecated */ + gate?: string; + interval?: string; + name: string; + on?: string; + pool?: string; + rig?: string; + schedule?: string; + scoped_name: string; + timeout?: string; + /** Format: int64 */ + timeout_ms: number; + trigger?: string; + type: string; + }; + OrdersFeedBody: { + items: components["schemas"]["MonitorFeedItemResponse"][] | null; + partial: boolean; + partial_errors?: string[] | null; + }; + OutboundEventPayload: { + conversation_id: string; + message_id: string; + provider: string; + session: string; + }; + OutboundResult: { + DeliveryContext: components["schemas"]["DeliveryContextRecord"]; + Receipt: components["schemas"]["PublishReceipt"]; + TranscriptEntry: components["schemas"]["ConversationTranscriptRecord"]; + }; + OutputTurn: { + role: string; + text: string; + timestamp?: string; + }; + PackListBody: { + /** @description Registered packs. */ + packs: components["schemas"]["PackResponse"][] | null; + }; + PackResponse: { + name: string; + path?: string; + ref?: string; + source?: string; + }; + PaginationInfo: { + has_older_messages: boolean; + /** Format: int64 */ + returned_message_count: number; + /** Format: int64 */ + total_compactions: number; + /** Format: int64 */ + total_message_count: number; + truncated_before_message?: string; + }; + PatchDeletedResponseBody: { + /** @description Agent patch qualified name. */ + agent_patch?: string; + /** @description Provider patch name. */ + provider_patch?: string; + /** @description Rig patch name. */ + rig_patch?: string; + /** + * @description Operation result. + * @example deleted + */ + status: string; + }; + PatchOKResponseBody: { + /** @description Agent patch qualified name. */ + agent_patch?: string; + /** @description Provider patch name. */ + provider_patch?: string; + /** @description Rig patch name. */ + rig_patch?: string; + /** + * @description Operation result. + * @example ok + */ + status: string; + }; + PendingInteraction: { + kind: string; + metadata?: { + [key: string]: string; + }; + options?: string[] | null; + prompt?: string; + request_id: string; + }; + PoolOverride: { + Check: string | null; + DrainTimeout: string | null; + /** Format: int64 */ + Max: number | null; + /** Format: int64 */ + Min: number | null; + OnBoot: string | null; + OnDeath: string | null; + }; + ProviderCreateInputBody: { + /** @description ACP transport command arguments override. */ + acp_args?: string[] | null; + /** @description ACP transport command binary override. */ + acp_command?: string; + /** @description Command arguments. */ + args?: string[] | null; + /** @description Arguments appended after inherited/base args. */ + args_append?: string[] | null; + /** @description Optional provider base for inheritance. */ + base?: string; + /** @description Provider command binary. Omit for base-only descendants. */ + command?: string; + /** @description Human-readable display name. */ + display_name?: string; + /** @description Environment variables. */ + env?: { + [key: string]: string; + }; + /** @description Provider name. */ + name: string; + /** @description Options schema merge mode across inheritance chain. */ + options_schema_merge?: string; + /** @description Flag for prompt delivery. */ + prompt_flag?: string; + /** @description Prompt delivery mode. */ + prompt_mode?: string; + /** + * Format: int64 + * @description Milliseconds to wait before probing readiness. + */ + ready_delay_ms?: number; + }; + ProviderCreatedOutputBody: { + /** @description Created provider name. */ + provider: string; + /** + * @description Operation result. + * @example created + */ + status: string; + }; + ProviderOptionDTO: { + choices: components["schemas"]["OptionChoiceDTO"][] | null; + default: string; + key: string; + label: string; + type: string; + }; + ProviderPatch: { + ACPArgs: string[] | null; + ACPCommand: string | null; + Args: string[] | null; + ArgsAppend: string[] | null; + Base: string | null; + Command: string | null; + Env: { + [key: string]: string; + }; + EnvRemove: string[] | null; + Name: string; + OptionsSchemaMerge: string | null; + PromptFlag: string | null; + PromptMode: string | null; + /** Format: int64 */ + ReadyDelayMs: number | null; + Replace: boolean; + }; + ProviderPatchSetInputBody: { + /** @description Override ACP transport command arguments. */ + acp_args?: string[] | null; + /** @description Override ACP transport command binary. */ + acp_command?: string; + /** @description Override command arguments. */ + args?: string[] | null; + /** @description Override command binary. */ + command?: string; + /** @description Override environment variables. */ + env?: { + [key: string]: string; + }; + /** @description Provider name. */ + name?: string; + /** @description Override prompt flag. */ + prompt_flag?: string; + /** @description Override prompt delivery mode. */ + prompt_mode?: string; + /** + * Format: int64 + * @description Override ready delay in milliseconds. + */ + ready_delay_ms?: number; + }; + ProviderPublicListBody: { + /** @description The list of browser-safe provider summaries. */ + items: components["schemas"]["ProviderPublicResponse"][] | null; + /** @description Cursor for the next page of results. */ + next_cursor?: string; + /** + * Format: int64 + * @description Total number of providers in the list. + */ + total: number; + }; + ProviderPublicResponse: { + builtin: boolean; + city_level: boolean; + display_name?: string; + effective_defaults?: { + [key: string]: string; + }; + name: string; + options_schema?: components["schemas"]["ProviderOptionDTO"][] | null; + }; + ProviderReadiness: { + detail?: string; + display_name: string; + status: string; + }; + ProviderReadinessResponse: { + providers: { + [key: string]: components["schemas"]["ProviderReadiness"]; + }; + }; + ProviderResponse: { + acp_args?: string[]; + acp_command?: string; + args?: string[] | null; + builtin: boolean; + city_level: boolean; + command?: string; + display_name?: string; + env?: { + [key: string]: string; + }; + name: string; + prompt_flag?: string; + prompt_mode?: string; + /** Format: int64 */ + ready_delay_ms?: number; + }; + ProviderSpecJSON: { + acp_args?: string[]; + acp_command?: string; + args?: string[] | null; + command?: string; + display_name?: string; + env?: { + [key: string]: string; + }; + prompt_flag?: string; + prompt_mode?: string; + /** Format: int64 */ + ready_delay_ms?: number; + }; + ProviderUpdateInputBody: { + /** @description ACP transport command arguments override. */ + acp_args?: string[] | null; + /** @description ACP transport command binary override. */ + acp_command?: string; + /** @description Command arguments. */ + args?: string[] | null; + /** @description Arguments appended after inherited/base args. */ + args_append?: string[] | null; + /** @description Provider base for inheritance. */ + base?: string; + /** @description Provider command binary. */ + command?: string; + /** @description Human-readable display name. */ + display_name?: string; + /** @description Environment variables. */ + env?: { + [key: string]: string; + }; + /** @description Options schema merge mode across inheritance chain. */ + options_schema_merge?: string; + /** @description Flag for prompt delivery. */ + prompt_flag?: string; + /** @description Prompt delivery mode. */ + prompt_mode?: string; + /** + * Format: int64 + * @description Milliseconds to wait before probing readiness. + */ + ready_delay_ms?: number; + }; + PublishReceipt: { + Conversation: components["schemas"]["ConversationRef"]; + Delivered: boolean; + FailureKind: string; + MessageID: string; + Metadata: { + [key: string]: string; + }; + /** Format: int64 */ + RetryAfter: number; + }; + ReadinessItem: { + detail?: string; + display_name: string; + kind: string; + name: string; + status: string; + }; + ReadinessResponse: { + items: { + [key: string]: components["schemas"]["ReadinessItem"]; + }; + }; + RigActionBody: { + /** @description Action that was performed. */ + action: string; + /** @description Agents that failed to stop (restart only). */ + failed?: string[] | null; + /** @description Agents that were killed (restart only). */ + killed?: string[] | null; + /** @description Rig name. */ + rig: string; + /** + * @description Operation result (ok, partial, failed). + * @example ok + */ + status: string; + }; + RigCreateInputBody: { + /** @description Rig name. */ + name: string; + /** @description Filesystem path. */ + path: string; + /** @description Session name prefix. */ + prefix?: string; + }; + RigCreatedOutputBody: { + /** @description Created rig name. */ + rig: string; + /** + * @description Operation result. + * @example created + */ + status: string; + }; + RigPatch: { + Name: string; + Path: string | null; + Prefix: string | null; + Suspended: boolean | null; + }; + RigPatchSetInputBody: { + /** @description Rig name. */ + name?: string; + /** @description Override filesystem path. */ + path?: string; + /** @description Override bead ID prefix. */ + prefix?: string; + /** @description Override suspended state. */ + suspended?: boolean; + }; + RigResponse: { + /** Format: int64 */ + agent_count: number; + git?: components["schemas"]["GitStatus"]; + /** Format: date-time */ + last_activity?: string; + name: string; + path: string; + prefix?: string; + /** Format: int64 */ + running_count: number; + suspended: boolean; + }; + RigUpdateInputBody: { + /** @description Filesystem path. */ + path?: string; + /** @description Session name prefix. */ + prefix?: string; + /** @description Whether rig is suspended. */ + suspended?: boolean; + }; + ScopeGroup: Record; + ServiceRestartOutputBody: { + /** + * @description Action performed. + * @example restart + */ + action: string; + /** @description Service name. */ + service: string; + /** + * @description Operation result. + * @example ok + */ + status: string; + }; + SessionActivityEvent: { + /** + * @description Session activity state: 'idle' or 'in-turn'. + * @example idle + */ + activity: string; + }; + SessionAgentGetResponse: { + messages: unknown[] | null; + status?: string; + }; + SessionAgentListResponse: { + agents: components["schemas"]["AgentMapping"][] | null; + }; + SessionBindingRecord: { + /** Format: int64 */ + BindingGeneration: number; + /** Format: date-time */ + BoundAt: string; + Conversation: components["schemas"]["ConversationRef"]; + /** Format: date-time */ + ExpiresAt: string | null; + ID: string; + Metadata: { + [key: string]: string; + }; + /** Format: int64 */ + SchemaVersion: number; + SessionID: string; + Status: components["schemas"]["BindingStatus"]; + }; + SessionCreateBody: { + /** @description Optional session alias. */ + alias?: string; + /** @description Create session asynchronously (agent only). */ + async?: boolean; + /** @description Session target kind: agent or provider. */ + kind?: string; + /** @description Initial message to send to the session. */ + message?: string; + /** @description Agent or provider name. */ + name?: string; + /** @description Provider/agent option overrides. */ + options?: { + [key: string]: string; + }; + /** @description Opaque project context identifier. */ + project_id?: string; + /** @description Deprecated: use alias. */ + session_name?: string; + /** @description Session title. */ + title?: string; + }; + SessionInfo: { + attached: boolean; + /** Format: date-time */ + last_activity?: string; + name: string; + }; + SessionMessageInputBody: { + /** @description Message text to send. */ + message: string; + }; + SessionMessageOutputBody: { + /** @description Session ID. */ + id: string; + /** + * @description Operation result. + * @example accepted + */ + status: string; + }; + SessionPatchBody: { + /** @description Session alias. Empty string clears the alias. */ + alias?: string; + /** @description Session title. If provided, must be non-empty. */ + title?: string; + }; + SessionPendingResponse: { + pending?: components["schemas"]["PendingInteraction"]; + supported: boolean; + }; + /** + * Session raw transcript frame + * @description Provider-native transcript frame. Gas City forwards the exact JSON the provider wrote to its session log, so the shape is provider-specific and can be any JSON value. The producing provider is identified by the Provider field on the enclosing envelope; consumers dispatch per-provider frame parsing keyed by that identifier. + */ + SessionRawMessageFrame: unknown; + SessionRenameInputBody: { + /** @description New session title. */ + title: string; + }; + SessionRespondInputBody: { + /** @description Response action (e.g. allow, deny). */ + action: string; + /** @description Optional response metadata. */ + metadata?: { + [key: string]: string; + }; + /** @description Pending interaction request ID (optional). */ + request_id?: string; + /** @description Optional response text. */ + text?: string; + }; + SessionRespondOutputBody: { + /** @description Session ID. */ + id: string; + /** + * @description Operation result. + * @example accepted + */ + status: string; + }; + SessionResponse: { + active_bead?: string; + activity?: string; + alias?: string; + attached: boolean; + configured_named_session?: boolean; + /** Format: int64 */ + context_pct?: number; + /** Format: int64 */ + context_window?: number; + created_at: string; + display_name?: string; + id: string; + kind?: string; + last_active?: string; + last_output?: string; + metadata?: { + [key: string]: string; + }; + model?: string; + options?: { + [key: string]: string; + }; + pool?: string; + provider: string; + reason?: string; + rig?: string; + running: boolean; + session_name: string; + state: string; + submission_capabilities?: components["schemas"]["SubmissionCapabilities"]; + template: string; + title: string; + }; + /** + * Session stream lifecycle event + * @description Non-message events emitted on the session SSE stream: activity transitions, pending interactions, and keepalive heartbeats. The concrete variant is identified by the SSE event name. + */ + SessionStreamCommonEvent: components["schemas"]["SessionActivityEvent"] | components["schemas"]["PendingInteraction"] | components["schemas"]["HeartbeatEvent"]; + SessionStreamMessageEvent: { + format: string; + id: string; + pagination?: components["schemas"]["PaginationInfo"]; + /** @description Producing provider identifier (claude, codex, gemini, open-code, etc.). */ + provider: string; + template: string; + turns: components["schemas"]["OutputTurn"][] | null; + }; + SessionStreamRawMessageEvent: { + format: string; + id: string; + /** @description Provider-native transcript frames, emitted verbatim as the provider wrote them. */ + messages: components["schemas"]["SessionRawMessageFrame"][] | null; + pagination?: components["schemas"]["PaginationInfo"]; + /** @description Producing provider identifier (claude, codex, gemini, open-code, etc.). Consumers use this to dispatch per-provider frame parsing. */ + provider: string; + template: string; + }; + SessionSubmitInputBody: { + /** + * @description Submit intent; empty defaults to "default". + * @enum {unknown} + */ + intent?: components["schemas"]["SubmitIntent"]; + /** @description Message text to submit. */ + message: string; + }; + SessionSubmitOutputBody: { + /** @description Session ID. */ + id: string; + /** @description Resolved submit intent. */ + intent: string; + /** @description Whether the message was queued. */ + queued: boolean; + /** + * @description Operation result. + * @example accepted + */ + status: string; + }; + SessionTranscriptGetResponse: { + /** @description conversation, text, or raw. */ + format: string; + id: string; + /** @description Populated for raw format; provider-native frames emitted verbatim as the provider wrote them. */ + messages?: components["schemas"]["SessionRawMessageFrame"][] | null; + pagination?: components["schemas"]["PaginationInfo"]; + /** @description Producing provider identifier (claude, codex, gemini, open-code, etc.). Consumers use this to dispatch per-provider frame parsing. */ + provider: string; + template: string; + /** @description Populated for conversation/text formats. */ + turns?: components["schemas"]["OutputTurn"][] | null; + }; + SlingInputBody: { + /** @description Bead ID to attach a formula to. */ + attached_bead_id?: string; + /** @description Bead ID to sling. */ + bead?: string; + /** @description Bypass cross-rig guards; for direct bead routes, also bypass missing-bead validation. Formula-backed graph routes may replace existing live workflow roots but still require the source bead to exist. */ + force?: boolean; + /** @description Formula name for workflow launch. */ + formula?: string; + /** @description Rig name. */ + rig?: string; + /** @description Scope kind (city or rig). */ + scope_kind?: string; + /** @description Scope reference. */ + scope_ref?: string; + /** @description Target agent or pool. */ + target: string; + /** @description Workflow title. */ + title?: string; + /** @description Formula variables. */ + vars?: { + [key: string]: string; + }; + }; + SlingResponse: { + attached_bead_id?: string; + bead?: string; + formula?: string; + mode?: string; + root_bead_id?: string; + status: string; + target: string; + warnings?: string[] | null; + workflow_id?: string; + }; + Status: { + allow_websockets?: boolean; + hostname?: string; + kind?: string; + local_state: string; + mount_path: string; + publication_state: string; + publish_mode: string; + reason?: string; + service_name: string; + state?: string; + state_root: string; + /** Format: date-time */ + updated_at: string; + url?: string; + visibility?: string; + workflow_contract?: string; + }; + StatusAgentCounts: { + /** + * Format: int64 + * @description Number of quarantined agents. + */ + quarantined: number; + /** + * Format: int64 + * @description Number of running agents. + */ + running: number; + /** + * Format: int64 + * @description Number of suspended agents. + */ + suspended: number; + /** + * Format: int64 + * @description Total number of agents. + */ + total: number; + }; + StatusBody: { + /** + * Format: int64 + * @description Total agent count (deprecated, use agents.total). + */ + agent_count: number; + /** @description Agent state counts. */ + agents: components["schemas"]["StatusAgentCounts"]; + /** @description Mail counts. */ + mail: components["schemas"]["StatusMailCounts"]; + /** @description City name. */ + name: string; + /** @description City directory path. */ + path: string; + /** + * Format: int64 + * @description Total rig count (deprecated, use rigs.total). + */ + rig_count: number; + /** @description Rig state counts. */ + rigs: components["schemas"]["StatusRigCounts"]; + /** + * Format: int64 + * @description Number of running agent processes. + */ + running: number; + /** @description Whether the city is suspended. */ + suspended: boolean; + /** + * Format: int64 + * @description Server uptime in seconds. + */ + uptime_sec: number; + /** @description Server version. */ + version?: string; + /** @description Work item counts. */ + work: components["schemas"]["StatusWorkCounts"]; + }; + StatusMailCounts: { + /** + * Format: int64 + * @description Total number of messages. + */ + total: number; + /** + * Format: int64 + * @description Number of unread messages. + */ + unread: number; + }; + StatusRigCounts: { + /** + * Format: int64 + * @description Number of suspended rigs. + */ + suspended: number; + /** + * Format: int64 + * @description Total number of rigs. + */ + total: number; + }; + StatusWorkCounts: { + /** + * Format: int64 + * @description Number of in-progress work items. + */ + in_progress: number; + /** + * Format: int64 + * @description Number of open work items. + */ + open: number; + /** + * Format: int64 + * @description Number of ready work items. + */ + ready: number; + }; + SubmissionCapabilities: { + supports_follow_up: boolean; + supports_interrupt_now: boolean; + }; + /** + * @description Semantic delivery choice for a user message on a session submit request. + * @enum {string} + */ + SubmitIntent: "default" | "follow_up" | "interrupt_now"; + SupervisorCitiesOutputBody: { + /** @description Managed cities with status info. */ + items: components["schemas"]["CityInfo"][] | null; + /** + * Format: int64 + * @description Total count. + */ + total: number; + }; + SupervisorEventListOutputBody: { + items: components["schemas"]["WireTaggedEvent"][] | null; + /** Format: int64 */ + total: number; + }; + SupervisorHealthOutputBody: { + /** + * Format: int64 + * @description Cities currently running. + */ + cities_running: number; + /** + * Format: int64 + * @description Total managed cities. + */ + cities_total: number; + /** @description First-city startup info for single-city deployments. */ + startup?: components["schemas"]["SupervisorStartup"]; + /** @description Health status ("ok"). */ + status: string; + /** + * Format: int64 + * @description Supervisor uptime in seconds. + */ + uptime_sec: number; + /** @description Supervisor version. */ + version: string; + }; + SupervisorStartup: { + /** @description Current phase (when not ready). */ + phase?: string; + /** @description Phases completed so far. */ + phases_completed?: string[] | null; + /** @description True when the city is running. */ + ready: boolean; + }; + TaggedEventStreamEnvelope: { + actor: string; + city: string; + message?: string; + payload?: components["schemas"]["EventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + type: string; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** + * @description Direction of a transcript entry. + * @enum {string} + */ + TranscriptMessageKind: "inbound" | "outbound"; + /** + * @description Provenance of a transcript entry (freshly observed vs. replayed from persisted history). + * @enum {string} + */ + TranscriptProvenance: "live" | "hydrated"; + /** + * Typed city event stream envelope + * @description Discriminated union of city event stream envelopes. Each variant constrains the envelope type and payload schema together. + */ + TypedEventStreamEnvelope: components["schemas"]["TypedEventStreamEnvelopeBeadClosed"] | components["schemas"]["TypedEventStreamEnvelopeBeadCreated"] | components["schemas"]["TypedEventStreamEnvelopeBeadUpdated"] | components["schemas"]["TypedEventStreamEnvelopeCityCreated"] | components["schemas"]["TypedEventStreamEnvelopeCityInitFailed"] | components["schemas"]["TypedEventStreamEnvelopeCityReady"] | components["schemas"]["TypedEventStreamEnvelopeCityResumed"] | components["schemas"]["TypedEventStreamEnvelopeCitySuspended"] | components["schemas"]["TypedEventStreamEnvelopeCityUnregisterFailed"] | components["schemas"]["TypedEventStreamEnvelopeCityUnregisterRequested"] | components["schemas"]["TypedEventStreamEnvelopeCityUnregistered"] | components["schemas"]["TypedEventStreamEnvelopeControllerStarted"] | components["schemas"]["TypedEventStreamEnvelopeControllerStopped"] | components["schemas"]["TypedEventStreamEnvelopeConvoyClosed"] | components["schemas"]["TypedEventStreamEnvelopeConvoyCreated"] | components["schemas"]["TypedEventStreamEnvelopeExtmsgAdapterAdded"] | components["schemas"]["TypedEventStreamEnvelopeExtmsgAdapterRemoved"] | components["schemas"]["TypedEventStreamEnvelopeExtmsgBound"] | components["schemas"]["TypedEventStreamEnvelopeExtmsgGroupCreated"] | components["schemas"]["TypedEventStreamEnvelopeExtmsgInbound"] | components["schemas"]["TypedEventStreamEnvelopeExtmsgOutbound"] | components["schemas"]["TypedEventStreamEnvelopeExtmsgUnbound"] | components["schemas"]["TypedEventStreamEnvelopeMailArchived"] | components["schemas"]["TypedEventStreamEnvelopeMailDeleted"] | components["schemas"]["TypedEventStreamEnvelopeMailMarkedRead"] | components["schemas"]["TypedEventStreamEnvelopeMailMarkedUnread"] | components["schemas"]["TypedEventStreamEnvelopeMailRead"] | components["schemas"]["TypedEventStreamEnvelopeMailReplied"] | components["schemas"]["TypedEventStreamEnvelopeMailSent"] | components["schemas"]["TypedEventStreamEnvelopeOrderCompleted"] | components["schemas"]["TypedEventStreamEnvelopeOrderFailed"] | components["schemas"]["TypedEventStreamEnvelopeOrderFired"] | components["schemas"]["TypedEventStreamEnvelopeProviderSwapped"] | components["schemas"]["TypedEventStreamEnvelopeSessionCrashed"] | components["schemas"]["TypedEventStreamEnvelopeSessionDraining"] | components["schemas"]["TypedEventStreamEnvelopeSessionIdleKilled"] | components["schemas"]["TypedEventStreamEnvelopeSessionQuarantined"] | components["schemas"]["TypedEventStreamEnvelopeSessionStopped"] | components["schemas"]["TypedEventStreamEnvelopeSessionSuspended"] | components["schemas"]["TypedEventStreamEnvelopeSessionUndrained"] | components["schemas"]["TypedEventStreamEnvelopeSessionUpdated"] | components["schemas"]["TypedEventStreamEnvelopeSessionWoke"] | components["schemas"]["TypedEventStreamEnvelopeWorkerOperation"]; + /** TypedEventStreamEnvelope bead.closed */ + TypedEventStreamEnvelopeBeadClosed: { + actor: string; + message?: string; + payload: components["schemas"]["BeadEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "bead.closed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope bead.created */ + TypedEventStreamEnvelopeBeadCreated: { + actor: string; + message?: string; + payload: components["schemas"]["BeadEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "bead.created"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope bead.updated */ + TypedEventStreamEnvelopeBeadUpdated: { + actor: string; + message?: string; + payload: components["schemas"]["BeadEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "bead.updated"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope city.created */ + TypedEventStreamEnvelopeCityCreated: { + actor: string; + message?: string; + payload: components["schemas"]["CityLifecyclePayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.created"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope city.init_failed */ + TypedEventStreamEnvelopeCityInitFailed: { + actor: string; + message?: string; + payload: components["schemas"]["CityLifecyclePayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.init_failed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope city.ready */ + TypedEventStreamEnvelopeCityReady: { + actor: string; + message?: string; + payload: components["schemas"]["CityLifecyclePayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.ready"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope city.resumed */ + TypedEventStreamEnvelopeCityResumed: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.resumed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope city.suspended */ + TypedEventStreamEnvelopeCitySuspended: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.suspended"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope city.unregister_failed */ + TypedEventStreamEnvelopeCityUnregisterFailed: { + actor: string; + message?: string; + payload: components["schemas"]["CityLifecyclePayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.unregister_failed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope city.unregister_requested */ + TypedEventStreamEnvelopeCityUnregisterRequested: { + actor: string; + message?: string; + payload: components["schemas"]["CityLifecyclePayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.unregister_requested"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope city.unregistered */ + TypedEventStreamEnvelopeCityUnregistered: { + actor: string; + message?: string; + payload: components["schemas"]["CityLifecyclePayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.unregistered"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope controller.started */ + TypedEventStreamEnvelopeControllerStarted: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "controller.started"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope controller.stopped */ + TypedEventStreamEnvelopeControllerStopped: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "controller.stopped"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope convoy.closed */ + TypedEventStreamEnvelopeConvoyClosed: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "convoy.closed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope convoy.created */ + TypedEventStreamEnvelopeConvoyCreated: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "convoy.created"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope extmsg.adapter_added */ + TypedEventStreamEnvelopeExtmsgAdapterAdded: { + actor: string; + message?: string; + payload: components["schemas"]["AdapterEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.adapter_added"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope extmsg.adapter_removed */ + TypedEventStreamEnvelopeExtmsgAdapterRemoved: { + actor: string; + message?: string; + payload: components["schemas"]["AdapterEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.adapter_removed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope extmsg.bound */ + TypedEventStreamEnvelopeExtmsgBound: { + actor: string; + message?: string; + payload: components["schemas"]["BoundEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.bound"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope extmsg.group_created */ + TypedEventStreamEnvelopeExtmsgGroupCreated: { + actor: string; + message?: string; + payload: components["schemas"]["GroupCreatedEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.group_created"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope extmsg.inbound */ + TypedEventStreamEnvelopeExtmsgInbound: { + actor: string; + message?: string; + payload: components["schemas"]["InboundEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.inbound"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope extmsg.outbound */ + TypedEventStreamEnvelopeExtmsgOutbound: { + actor: string; + message?: string; + payload: components["schemas"]["OutboundEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.outbound"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope extmsg.unbound */ + TypedEventStreamEnvelopeExtmsgUnbound: { + actor: string; + message?: string; + payload: components["schemas"]["UnboundEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.unbound"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope mail.archived */ + TypedEventStreamEnvelopeMailArchived: { + actor: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.archived"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope mail.deleted */ + TypedEventStreamEnvelopeMailDeleted: { + actor: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.deleted"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope mail.marked_read */ + TypedEventStreamEnvelopeMailMarkedRead: { + actor: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.marked_read"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope mail.marked_unread */ + TypedEventStreamEnvelopeMailMarkedUnread: { + actor: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.marked_unread"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope mail.read */ + TypedEventStreamEnvelopeMailRead: { + actor: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.read"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope mail.replied */ + TypedEventStreamEnvelopeMailReplied: { + actor: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.replied"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope mail.sent */ + TypedEventStreamEnvelopeMailSent: { + actor: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.sent"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope order.completed */ + TypedEventStreamEnvelopeOrderCompleted: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "order.completed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope order.failed */ + TypedEventStreamEnvelopeOrderFailed: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "order.failed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope order.fired */ + TypedEventStreamEnvelopeOrderFired: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "order.fired"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope provider.swapped */ + TypedEventStreamEnvelopeProviderSwapped: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "provider.swapped"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope session.crashed */ + TypedEventStreamEnvelopeSessionCrashed: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.crashed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope session.draining */ + TypedEventStreamEnvelopeSessionDraining: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.draining"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope session.idle_killed */ + TypedEventStreamEnvelopeSessionIdleKilled: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.idle_killed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope session.quarantined */ + TypedEventStreamEnvelopeSessionQuarantined: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.quarantined"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope session.stopped */ + TypedEventStreamEnvelopeSessionStopped: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.stopped"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope session.suspended */ + TypedEventStreamEnvelopeSessionSuspended: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.suspended"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope session.undrained */ + TypedEventStreamEnvelopeSessionUndrained: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.undrained"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope session.updated */ + TypedEventStreamEnvelopeSessionUpdated: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.updated"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope session.woke */ + TypedEventStreamEnvelopeSessionWoke: { + actor: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.woke"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedEventStreamEnvelope worker.operation */ + TypedEventStreamEnvelopeWorkerOperation: { + actor: string; + message?: string; + payload: components["schemas"]["WorkerOperationEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "worker.operation"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** + * Typed supervisor event stream envelope + * @description Discriminated union of supervisor event stream envelopes. Each variant constrains the envelope type and payload schema together and includes the source city. + */ + TypedTaggedEventStreamEnvelope: components["schemas"]["TypedTaggedEventStreamEnvelopeBeadClosed"] | components["schemas"]["TypedTaggedEventStreamEnvelopeBeadCreated"] | components["schemas"]["TypedTaggedEventStreamEnvelopeBeadUpdated"] | components["schemas"]["TypedTaggedEventStreamEnvelopeCityCreated"] | components["schemas"]["TypedTaggedEventStreamEnvelopeCityInitFailed"] | components["schemas"]["TypedTaggedEventStreamEnvelopeCityReady"] | components["schemas"]["TypedTaggedEventStreamEnvelopeCityResumed"] | components["schemas"]["TypedTaggedEventStreamEnvelopeCitySuspended"] | components["schemas"]["TypedTaggedEventStreamEnvelopeCityUnregisterFailed"] | components["schemas"]["TypedTaggedEventStreamEnvelopeCityUnregisterRequested"] | components["schemas"]["TypedTaggedEventStreamEnvelopeCityUnregistered"] | components["schemas"]["TypedTaggedEventStreamEnvelopeControllerStarted"] | components["schemas"]["TypedTaggedEventStreamEnvelopeControllerStopped"] | components["schemas"]["TypedTaggedEventStreamEnvelopeConvoyClosed"] | components["schemas"]["TypedTaggedEventStreamEnvelopeConvoyCreated"] | components["schemas"]["TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded"] | components["schemas"]["TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved"] | components["schemas"]["TypedTaggedEventStreamEnvelopeExtmsgBound"] | components["schemas"]["TypedTaggedEventStreamEnvelopeExtmsgGroupCreated"] | components["schemas"]["TypedTaggedEventStreamEnvelopeExtmsgInbound"] | components["schemas"]["TypedTaggedEventStreamEnvelopeExtmsgOutbound"] | components["schemas"]["TypedTaggedEventStreamEnvelopeExtmsgUnbound"] | components["schemas"]["TypedTaggedEventStreamEnvelopeMailArchived"] | components["schemas"]["TypedTaggedEventStreamEnvelopeMailDeleted"] | components["schemas"]["TypedTaggedEventStreamEnvelopeMailMarkedRead"] | components["schemas"]["TypedTaggedEventStreamEnvelopeMailMarkedUnread"] | components["schemas"]["TypedTaggedEventStreamEnvelopeMailRead"] | components["schemas"]["TypedTaggedEventStreamEnvelopeMailReplied"] | components["schemas"]["TypedTaggedEventStreamEnvelopeMailSent"] | components["schemas"]["TypedTaggedEventStreamEnvelopeOrderCompleted"] | components["schemas"]["TypedTaggedEventStreamEnvelopeOrderFailed"] | components["schemas"]["TypedTaggedEventStreamEnvelopeOrderFired"] | components["schemas"]["TypedTaggedEventStreamEnvelopeProviderSwapped"] | components["schemas"]["TypedTaggedEventStreamEnvelopeSessionCrashed"] | components["schemas"]["TypedTaggedEventStreamEnvelopeSessionDraining"] | components["schemas"]["TypedTaggedEventStreamEnvelopeSessionIdleKilled"] | components["schemas"]["TypedTaggedEventStreamEnvelopeSessionQuarantined"] | components["schemas"]["TypedTaggedEventStreamEnvelopeSessionStopped"] | components["schemas"]["TypedTaggedEventStreamEnvelopeSessionSuspended"] | components["schemas"]["TypedTaggedEventStreamEnvelopeSessionUndrained"] | components["schemas"]["TypedTaggedEventStreamEnvelopeSessionUpdated"] | components["schemas"]["TypedTaggedEventStreamEnvelopeSessionWoke"] | components["schemas"]["TypedTaggedEventStreamEnvelopeWorkerOperation"]; + /** TypedTaggedEventStreamEnvelope bead.closed */ + TypedTaggedEventStreamEnvelopeBeadClosed: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["BeadEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "bead.closed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope bead.created */ + TypedTaggedEventStreamEnvelopeBeadCreated: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["BeadEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "bead.created"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope bead.updated */ + TypedTaggedEventStreamEnvelopeBeadUpdated: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["BeadEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "bead.updated"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope city.created */ + TypedTaggedEventStreamEnvelopeCityCreated: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["CityLifecyclePayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.created"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope city.init_failed */ + TypedTaggedEventStreamEnvelopeCityInitFailed: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["CityLifecyclePayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.init_failed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope city.ready */ + TypedTaggedEventStreamEnvelopeCityReady: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["CityLifecyclePayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.ready"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope city.resumed */ + TypedTaggedEventStreamEnvelopeCityResumed: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.resumed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope city.suspended */ + TypedTaggedEventStreamEnvelopeCitySuspended: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.suspended"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope city.unregister_failed */ + TypedTaggedEventStreamEnvelopeCityUnregisterFailed: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["CityLifecyclePayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.unregister_failed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope city.unregister_requested */ + TypedTaggedEventStreamEnvelopeCityUnregisterRequested: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["CityLifecyclePayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.unregister_requested"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope city.unregistered */ + TypedTaggedEventStreamEnvelopeCityUnregistered: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["CityLifecyclePayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "city.unregistered"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope controller.started */ + TypedTaggedEventStreamEnvelopeControllerStarted: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "controller.started"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope controller.stopped */ + TypedTaggedEventStreamEnvelopeControllerStopped: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "controller.stopped"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope convoy.closed */ + TypedTaggedEventStreamEnvelopeConvoyClosed: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "convoy.closed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope convoy.created */ + TypedTaggedEventStreamEnvelopeConvoyCreated: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "convoy.created"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope extmsg.adapter_added */ + TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["AdapterEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.adapter_added"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope extmsg.adapter_removed */ + TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["AdapterEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.adapter_removed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope extmsg.bound */ + TypedTaggedEventStreamEnvelopeExtmsgBound: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["BoundEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.bound"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope extmsg.group_created */ + TypedTaggedEventStreamEnvelopeExtmsgGroupCreated: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["GroupCreatedEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.group_created"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope extmsg.inbound */ + TypedTaggedEventStreamEnvelopeExtmsgInbound: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["InboundEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.inbound"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope extmsg.outbound */ + TypedTaggedEventStreamEnvelopeExtmsgOutbound: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["OutboundEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.outbound"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope extmsg.unbound */ + TypedTaggedEventStreamEnvelopeExtmsgUnbound: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["UnboundEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "extmsg.unbound"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope mail.archived */ + TypedTaggedEventStreamEnvelopeMailArchived: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.archived"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope mail.deleted */ + TypedTaggedEventStreamEnvelopeMailDeleted: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.deleted"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope mail.marked_read */ + TypedTaggedEventStreamEnvelopeMailMarkedRead: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.marked_read"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope mail.marked_unread */ + TypedTaggedEventStreamEnvelopeMailMarkedUnread: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.marked_unread"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope mail.read */ + TypedTaggedEventStreamEnvelopeMailRead: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.read"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope mail.replied */ + TypedTaggedEventStreamEnvelopeMailReplied: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.replied"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope mail.sent */ + TypedTaggedEventStreamEnvelopeMailSent: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["MailEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "mail.sent"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope order.completed */ + TypedTaggedEventStreamEnvelopeOrderCompleted: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "order.completed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope order.failed */ + TypedTaggedEventStreamEnvelopeOrderFailed: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "order.failed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope order.fired */ + TypedTaggedEventStreamEnvelopeOrderFired: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "order.fired"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope provider.swapped */ + TypedTaggedEventStreamEnvelopeProviderSwapped: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "provider.swapped"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope session.crashed */ + TypedTaggedEventStreamEnvelopeSessionCrashed: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.crashed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope session.draining */ + TypedTaggedEventStreamEnvelopeSessionDraining: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.draining"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope session.idle_killed */ + TypedTaggedEventStreamEnvelopeSessionIdleKilled: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.idle_killed"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope session.quarantined */ + TypedTaggedEventStreamEnvelopeSessionQuarantined: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.quarantined"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope session.stopped */ + TypedTaggedEventStreamEnvelopeSessionStopped: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.stopped"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope session.suspended */ + TypedTaggedEventStreamEnvelopeSessionSuspended: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.suspended"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope session.undrained */ + TypedTaggedEventStreamEnvelopeSessionUndrained: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.undrained"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope session.updated */ + TypedTaggedEventStreamEnvelopeSessionUpdated: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.updated"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope session.woke */ + TypedTaggedEventStreamEnvelopeSessionWoke: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["NoPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "session.woke"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + /** TypedTaggedEventStreamEnvelope worker.operation */ + TypedTaggedEventStreamEnvelopeWorkerOperation: { + actor: string; + city: string; + message?: string; + payload: components["schemas"]["WorkerOperationEventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "worker.operation"; + workflow?: components["schemas"]["WorkflowEventProjection"]; + }; + UnboundEventPayload: { + /** Format: int64 */ + count: number; + session_id: string; + }; + WireEvent: { + actor: string; + message?: string; + payload?: components["schemas"]["EventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + type: string; + }; + WireTaggedEvent: { + actor: string; + city: string; + message?: string; + payload?: components["schemas"]["EventPayload"]; + /** Format: int64 */ + seq: number; + subject?: string; + /** Format: date-time */ + ts: string; + type: string; + }; + WorkerOperationEventPayload: { + delivered?: boolean; + /** Format: int64 */ + duration_ms: number; + error?: string; + /** Format: date-time */ + finished_at: string; + op_id: string; + operation: string; + provider?: string; + queued?: boolean; + result: string; + session_id?: string; + session_name?: string; + /** Format: date-time */ + started_at: string; + template?: string; + transport?: string; + }; + WorkflowAttemptSummary: { + /** Format: int64 */ + active_attempt: number; + /** Format: int64 */ + attempt_count: number; + /** Format: int64 */ + max_attempts?: number; + }; + WorkflowBeadResponse: { + assignee?: string; + /** Format: int64 */ + attempt?: number; + id: string; + kind: string; + logical_bead_id?: string; + metadata: { + [key: string]: string; + }; + scope_ref?: string; + status: string; + step_ref?: string; + title: string; + }; + WorkflowDeleteResponse: { + /** + * Format: int64 + * @description Number of beads closed. + */ + closed: number; + /** + * Format: int64 + * @description Number of beads deleted. + */ + deleted: number; + /** @description True when one or more teardown steps failed; Closed/Deleted still reflect what succeeded. */ + partial?: boolean; + /** @description Human-readable errors from failed teardown steps. */ + partial_errors?: string[] | null; + /** @description Workflow ID. */ + workflow_id: string; + }; + WorkflowDepResponse: { + from: string; + kind?: string; + to: string; + }; + WorkflowEventProjection: { + attempt_summary?: components["schemas"]["WorkflowAttemptSummary"]; + bead: components["schemas"]["WorkflowBeadResponse"]; + changed_fields: string[] | null; + /** Format: int64 */ + event_seq: number; + event_ts: string; + event_type: string; + logical_node_id: string; + requires_resync?: boolean; + root_bead_id: string; + root_store_ref: string; + scope_kind: string; + scope_ref: string; + type: string; + watch_generation: string; + workflow_id: string; + /** Format: int64 */ + workflow_seq: number; + }; + WorkflowSnapshotResponse: { + beads: components["schemas"]["WorkflowBeadResponse"][] | null; + deps: components["schemas"]["WorkflowDepResponse"][] | null; + logical_edges: components["schemas"]["WorkflowDepResponse"][] | null; + logical_nodes: components["schemas"]["LogicalNode"][] | null; + partial: boolean; + resolved_root_store: string; + root_bead_id: string; + root_store_ref: string; + scope_groups: components["schemas"]["ScopeGroup"][] | null; + scope_kind: string; + scope_ref: string; + /** Format: int64 */ + snapshot_event_seq?: number; + /** Format: int64 */ + snapshot_version: number; + stores_scanned: string[] | null; + workflow_id: string; + }; + WorkspaceResponse: { + declared_name?: string; + declared_prefix?: string; + name: string; + prefix?: string; + provider?: string; + session_template?: string; + suspended: boolean; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: { + /** @description Opaque per-response identifier assigned by the server for log correlation. Every response carries this header. */ + "X-GC-Request-Id": string; + }; + pathItems: never; +} +export type $defs = Record; +export interface operations { + "get-health": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SupervisorHealthOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-cities": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SupervisorCitiesOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CityCreateRequest"]; + }; + }; + responses: { + /** @description Accepted */ + 202: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CityCreateResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CityGetResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "patch-v0-city-by-city-name": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CityPatchInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-agent-by-base": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent name (unqualified, no rig). */ + base: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AgentResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-agent-by-base": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent name (unqualified). */ + base: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "patch-v0-city-by-city-name-agent-by-base": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent name (unqualified). */ + base: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AgentUpdateInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-agent-by-base-output": { + parameters: { + query?: { + /** @description Number of recent compaction segments to return. This API parameter keeps compaction-segment semantics even though gc session logs --tail counts displayed transcript entries. Omit for the endpoint default (usually 1); 0 returns all segments; N>0 returns the last N. */ + tail?: string; + /** @description Message UUID cursor for loading older messages. */ + before?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent base name. */ + base: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AgentOutputResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "stream-agent-output": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent base name. */ + base: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + /** @description Agent runtime status at the time streaming began. Emitted as "stopped" when the agent is not running (the stream then serves replayed transcript from the session log). */ + "GC-Agent-Status"?: string; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "text/event-stream": ({ + data: components["schemas"]["HeartbeatEvent"]; + /** + * @description The event name. + * @constant + */ + event: "heartbeat"; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } | { + data: components["schemas"]["AgentOutputResponse"]; + /** + * @description The event name. + * @constant + */ + event: "turn"; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + })[]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-agent-by-base-by-action": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent name (unqualified). */ + base: string; + /** @description Action to perform. */ + action: "suspend" | "resume"; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-agent-by-dir-by-base": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent directory (rig name). */ + dir: string; + /** @description Agent base name. */ + base: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AgentResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-agent-by-dir-by-base": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent directory (rig name). */ + dir: string; + /** @description Agent base name. */ + base: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "patch-v0-city-by-city-name-agent-by-dir-by-base": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent directory (rig name). */ + dir: string; + /** @description Agent base name. */ + base: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AgentUpdateQualifiedInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-agent-by-dir-by-base-output": { + parameters: { + query?: { + /** @description Number of recent compaction segments to return. This API parameter keeps compaction-segment semantics even though gc session logs --tail counts displayed transcript entries. Omit for the endpoint default (usually 1); 0 returns all segments; N>0 returns the last N. */ + tail?: string; + /** @description Message UUID cursor for loading older messages. */ + before?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent directory (rig name). */ + dir: string; + /** @description Agent base name. */ + base: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AgentOutputResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "stream-agent-output-qualified": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent directory (rig name). */ + dir: string; + /** @description Agent base name. */ + base: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + /** @description Agent runtime status at the time streaming began. Emitted as "stopped" when the agent is not running (the stream then serves replayed transcript from the session log). */ + "GC-Agent-Status"?: string; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "text/event-stream": ({ + data: components["schemas"]["HeartbeatEvent"]; + /** + * @description The event name. + * @constant + */ + event: "heartbeat"; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } | { + data: components["schemas"]["AgentOutputResponse"]; + /** + * @description The event name. + * @constant + */ + event: "turn"; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + })[]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-agent-by-dir-by-base-by-action": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent directory (rig name). */ + dir: string; + /** @description Agent base name. */ + base: string; + /** @description Action to perform. */ + action: "suspend" | "resume"; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-agents": { + parameters: { + query?: { + /** @description Event sequence number; when provided, blocks until a newer event arrives. */ + index?: string; + /** @description How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. */ + wait?: string; + /** @description Filter by pool name. */ + pool?: string; + /** @description Filter by rig name. */ + rig?: string; + /** @description Filter by running state. Omit to return all agents. */ + running?: "true" | "false"; + /** @description Include last output preview. */ + peek?: boolean; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyAgentResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "create-agent": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AgentCreateInputBody"]; + }; + }; + responses: { + /** @description Created */ + 201: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AgentCreatedOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-bead-by-id": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Bead ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Bead"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-bead-by-id": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Bead ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "patch-v0-city-by-city-name-bead-by-id": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Bead ID. */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["BeadUpdateBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-bead-by-id-assign": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Bead ID. */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["BeadAssignInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: string; + }; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-bead-by-id-close": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Bead ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-bead-by-id-deps": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Bead ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BeadDepsResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-bead-by-id-reopen": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Bead ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-bead-by-id-update": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Bead ID. */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["BeadUpdateBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-beads": { + parameters: { + query?: { + /** @description Event sequence number; when provided, blocks until a newer event arrives. */ + index?: string; + /** @description How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. */ + wait?: string; + /** @description Pagination cursor from a previous response's next_cursor field. */ + cursor?: string; + /** @description Maximum number of results to return. 0 = server default. */ + limit?: number; + /** @description Filter by bead status. */ + status?: string; + /** @description Filter by bead type. */ + type?: string; + /** @description Filter by label. */ + label?: string; + /** @description Filter by assignee. */ + assignee?: string; + /** @description Filter by rig. */ + rig?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyBead"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "create-bead": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + /** @description Idempotency key for safe retries. */ + "Idempotency-Key"?: string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["BeadCreateInputBody"]; + }; + }; + responses: { + /** @description Created */ + 201: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Bead"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-beads-graph-by-root-id": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Root bead ID for the graph. */ + rootID: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BeadGraphResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-beads-ready": { + parameters: { + query?: { + /** @description Event sequence number; when provided, blocks until a newer event arrives. */ + index?: string; + /** @description How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. */ + wait?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyBead"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-config": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ConfigResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-config-explain": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ConfigExplainResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-config-validate": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ConfigValidateOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-convoy-by-id": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Convoy ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ConvoyGetResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-convoy-by-id": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Convoy ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-convoy-by-id-add": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Convoy ID. */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ConvoyAddInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-convoy-by-id-check": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Convoy ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ConvoyCheckResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-convoy-by-id-close": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Convoy ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-convoy-by-id-remove": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Convoy ID. */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ConvoyRemoveInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-convoys": { + parameters: { + query?: { + /** @description Event sequence number; when provided, blocks until a newer event arrives. */ + index?: string; + /** @description How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. */ + wait?: string; + /** @description Pagination cursor from a previous response's next_cursor field. */ + cursor?: string; + /** @description Maximum number of results to return. 0 = server default. */ + limit?: number; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyBead"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "create-convoy": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ConvoyCreateInputBody"]; + }; + }; + responses: { + /** @description Created */ + 201: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Bead"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-events": { + parameters: { + query?: { + /** @description Event sequence number; when provided, blocks until a newer event arrives. */ + index?: string; + /** @description How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. */ + wait?: string; + /** @description Pagination cursor from a previous response's next_cursor field. */ + cursor?: string; + /** @description Maximum number of results to return. 0 = server default. */ + limit?: number; + /** @description Filter by event type. */ + type?: string; + /** @description Filter by actor. */ + actor?: string; + /** @description Filter events since duration ago (Go duration string, e.g. 5m). */ + since?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyWireEvent"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "emit-event": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["EventEmitRequest"]; + }; + }; + responses: { + /** @description Created */ + 201: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EventEmitOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "stream-events": { + parameters: { + query?: { + /** @description Reconnect position: only deliver events after this sequence number. */ + after_seq?: string; + }; + header?: { + /** @description SSE reconnect position from the last received event ID. */ + "Last-Event-ID"?: string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "text/event-stream": ({ + data: components["schemas"]["TypedEventStreamEnvelope"]; + /** + * @description The event name. + * @constant + */ + event: "event"; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } | { + data: components["schemas"]["HeartbeatEvent"]; + /** + * @description The event name. + * @constant + */ + event: "heartbeat"; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + })[]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-extmsg-adapters": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyExtmsgAdapterInfo"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "register-extmsg-adapter": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ExtMsgAdapterRegisterInputBody"]; + }; + }; + responses: { + /** @description Created */ + 201: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ExtMsgAdapterRegisterOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-extmsg-adapters": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ExtMsgAdapterUnregisterInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-extmsg-bind": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ExtMsgBindInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionBindingRecord"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-extmsg-bindings": { + parameters: { + query?: { + /** @description Session ID to list bindings for. */ + session_id?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodySessionBindingRecord"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-extmsg-groups": { + parameters: { + query?: { + /** @description Scope ID. */ + scope_id?: string; + /** @description Provider name. */ + provider?: string; + /** @description Account ID. */ + account_id?: string; + /** @description Conversation ID. */ + conversation_id?: string; + /** @description Conversation kind. */ + kind?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ConversationGroupRecord"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "ensure-extmsg-group": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ExtMsgGroupEnsureInputBody"]; + }; + }; + responses: { + /** @description Created */ + 201: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ConversationGroupRecord"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-extmsg-inbound": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ExtMsgInboundInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InboundResult"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-extmsg-outbound": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ExtMsgOutboundInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OutboundResult"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-extmsg-participants": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ExtMsgParticipantUpsertInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ConversationGroupParticipant"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-extmsg-participants": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ExtMsgParticipantRemoveInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-extmsg-transcript": { + parameters: { + query?: { + /** @description Scope ID. */ + scope_id?: string; + /** @description Provider name. */ + provider?: string; + /** @description Account ID. */ + account_id?: string; + /** @description Conversation ID. */ + conversation_id?: string; + /** @description Parent conversation ID. */ + parent_conversation_id?: string; + /** @description Conversation kind. */ + kind?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyConversationTranscriptRecord"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-extmsg-transcript-ack": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ExtMsgTranscriptAckInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-extmsg-unbind": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ExtMsgUnbindInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ExtMsgUnbindBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-formula-by-name": { + parameters: { + query: { + /** @description Scope kind (city or rig). */ + scope_kind?: string; + /** @description Scope reference. */ + scope_ref?: string; + /** @description Target agent for preview compilation. */ + target: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Formula name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FormulaDetailResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-formulas": { + parameters: { + query?: { + /** @description Scope kind (city or rig). */ + scope_kind?: string; + /** @description Scope reference. */ + scope_ref?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FormulaListBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-formulas-feed": { + parameters: { + query?: { + /** @description Scope kind (city or rig). */ + scope_kind?: string; + /** @description Scope reference. */ + scope_ref?: string; + /** @description Maximum number of feed items to return. 0 = default. */ + limit?: number; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FormulaFeedBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-formulas-by-name": { + parameters: { + query: { + /** @description Scope kind (city or rig). */ + scope_kind?: string; + /** @description Scope reference. */ + scope_ref?: string; + /** @description Target agent for preview compilation. */ + target: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Formula name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FormulaDetailResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-formulas-by-name-preview": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Formula name. */ + name: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["FormulaPreviewBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FormulaDetailResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-formulas-by-name-runs": { + parameters: { + query?: { + /** @description Scope kind (city or rig). */ + scope_kind?: string; + /** @description Scope reference. */ + scope_ref?: string; + /** @description Maximum number of recent runs to return. 0 = default. */ + limit?: number; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Formula name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FormulaRunsResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-health": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HealthOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-mail": { + parameters: { + query?: { + /** @description Event sequence number; when provided, blocks until a newer event arrives. */ + index?: string; + /** @description How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. */ + wait?: string; + /** @description Pagination cursor from a previous response's next_cursor field. */ + cursor?: string; + /** @description Maximum number of results to return. 0 = server default. */ + limit?: number; + /** @description Filter by agent name. */ + agent?: string; + /** @description Filter by status (unread, all). */ + status?: string; + /** @description Filter by rig name. */ + rig?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MailListBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "send-mail": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + /** @description Idempotency key for safe retries. */ + "Idempotency-Key"?: string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["MailSendInputBody"]; + }; + }; + responses: { + /** @description Created */ + 201: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Message"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-mail-count": { + parameters: { + query?: { + /** @description Filter by agent name. */ + agent?: string; + /** @description Filter by rig name. */ + rig?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MailCountOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-mail-thread-by-id": { + parameters: { + query?: { + /** @description Filter by rig. */ + rig?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Thread ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MailListBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-mail-by-id": { + parameters: { + query?: { + /** @description Rig hint for O(1) lookup. */ + rig?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Message ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Message"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-mail-by-id": { + parameters: { + query?: { + /** @description Rig hint. */ + rig?: string; + }; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Message ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-mail-by-id-archive": { + parameters: { + query?: { + /** @description Rig hint. */ + rig?: string; + }; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Message ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-mail-by-id-mark-unread": { + parameters: { + query?: { + /** @description Rig hint. */ + rig?: string; + }; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Message ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-mail-by-id-read": { + parameters: { + query?: { + /** @description Rig hint. */ + rig?: string; + }; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Message ID. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "reply-mail": { + parameters: { + query?: { + /** @description Rig hint. */ + rig?: string; + }; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Message ID. */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["MailReplyInputBody"]; + }; + }; + responses: { + /** @description Created */ + 201: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Message"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-order-history-by-bead-id": { + parameters: { + query?: { + /** @description Store reference for disambiguating store-local bead IDs. */ + store_ref?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Bead ID for the order run. */ + bead_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OrderHistoryDetailResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-order-by-name": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Order name or scoped name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OrderResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-order-by-name-disable": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Order name or scoped name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-order-by-name-enable": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Order name or scoped name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-orders": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OrderListBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-orders-check": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OrderCheckListBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-orders-feed": { + parameters: { + query?: { + /** @description Scope kind (city or rig). */ + scope_kind?: string; + /** @description Scope reference. */ + scope_ref?: string; + /** @description Maximum number of feed items to return. */ + limit?: number; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OrdersFeedBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-orders-history": { + parameters: { + query: { + /** @description Scoped order name. */ + scoped_name: string; + /** @description Maximum number of history entries. 0 = default. */ + limit?: number; + /** @description Return entries before this RFC3339 timestamp. */ + before?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OrderHistoryListBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-packs": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PackListBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-patches-agent-by-base": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent patch name (unqualified). */ + base: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AgentPatch"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-patches-agent-by-base": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent patch name (unqualified). */ + base: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PatchDeletedResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-patches-agent-by-dir-by-base": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent directory (rig name). */ + dir: string; + /** @description Agent base name. */ + base: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AgentPatch"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-patches-agent-by-dir-by-base": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Agent directory (rig name). */ + dir: string; + /** @description Agent base name. */ + base: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PatchDeletedResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-patches-agents": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyAgentPatch"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "put-v0-city-by-city-name-patches-agents": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AgentPatchSetInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PatchOKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-patches-provider-by-name": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Provider patch name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProviderPatch"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-patches-provider-by-name": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Provider patch name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PatchDeletedResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-patches-providers": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyProviderPatch"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "put-v0-city-by-city-name-patches-providers": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ProviderPatchSetInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PatchOKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-patches-rig-by-name": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Rig patch name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RigPatch"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-patches-rig-by-name": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Rig patch name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PatchDeletedResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-patches-rigs": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyRigPatch"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "put-v0-city-by-city-name-patches-rigs": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RigPatchSetInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PatchOKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-provider-readiness": { + parameters: { + query?: { + /** @description Comma-separated provider names to check (default: claude,codex,gemini). */ + providers?: string; + /** @description Force fresh probe, bypassing cache. */ + fresh?: boolean; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProviderReadinessResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-provider-by-name": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Provider name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProviderResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-provider-by-name": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Provider name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "patch-v0-city-by-city-name-provider-by-name": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Provider name. */ + name: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ProviderUpdateInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-providers": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyProviderResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "create-provider": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ProviderCreateInputBody"]; + }; + }; + responses: { + /** @description Created */ + 201: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProviderCreatedOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-providers-public": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProviderPublicListBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-readiness": { + parameters: { + query?: { + /** @description Comma-separated readiness items to check (default: claude,codex,gemini,github_cli). */ + items?: string; + /** @description Force fresh probe, bypassing cache. */ + fresh?: boolean; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ReadinessResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-rig-by-name": { + parameters: { + query?: { + /** @description Include git status. */ + git?: boolean; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Rig name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RigResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-rig-by-name": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Rig name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "patch-v0-city-by-city-name-rig-by-name": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Rig name. */ + name: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RigUpdateInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-rig-by-name-by-action": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Rig name. */ + name: string; + /** @description Action to perform (suspend, resume, restart). */ + action: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RigActionBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-rigs": { + parameters: { + query?: { + /** @description Event sequence number; when provided, blocks until a newer event arrives. */ + index?: string; + /** @description How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. */ + wait?: string; + /** @description Include git status. */ + git?: boolean; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyRigResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "create-rig": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RigCreateInputBody"]; + }; + }; + responses: { + /** @description Created */ + 201: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RigCreatedOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-service-by-name": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Service name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Status"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-service-by-name-restart": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Service name. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ServiceRestartOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-services": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodyStatus"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-session-by-id": { + parameters: { + query?: { + /** @description Include last output preview. */ + peek?: boolean; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "patch-v0-city-by-city-name-session-by-id": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SessionPatchBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-session-by-id-agents": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionAgentListResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-session-by-id-agents-by-agent-id": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + /** @description Subagent ID within the session. */ + agentId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionAgentGetResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-session-by-id-close": { + parameters: { + query?: { + /** @description Permanently delete bead after closing. */ + delete?: boolean; + }; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-session-by-id-kill": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKWithIDResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "send-session-message": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SessionMessageInputBody"]; + }; + }; + responses: { + /** @description Accepted */ + 202: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionMessageOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-session-by-id-pending": { + parameters: { + query?: never; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionPendingResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-session-by-id-rename": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SessionRenameInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "respond-session": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SessionRespondInputBody"]; + }; + }; + responses: { + /** @description Accepted */ + 202: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionRespondOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-session-by-id-stop": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKWithIDResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "stream-session": { + parameters: { + query?: { + /** @description Transcript format: conversation (default) or raw. */ + format?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + /** @description Session state at the time streaming began (e.g. active, closed). */ + "GC-Session-State"?: string; + /** @description Runtime status at the time streaming began. Emitted as "stopped" when the session's underlying process is not running. */ + "GC-Session-Status"?: string; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "text/event-stream": ({ + data: components["schemas"]["SessionActivityEvent"]; + /** + * @description The event name. + * @constant + */ + event: "activity"; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } | { + data: components["schemas"]["HeartbeatEvent"]; + /** + * @description The event name. + * @constant + */ + event: "heartbeat"; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } | { + data: components["schemas"]["SessionStreamRawMessageEvent"]; + /** + * @description The event name. + * @constant + */ + event?: "message"; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } | { + data: components["schemas"]["PendingInteraction"]; + /** + * @description The event name. + * @constant + */ + event: "pending"; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + } | { + data: components["schemas"]["SessionStreamMessageEvent"]; + /** + * @description The event name. + * @constant + */ + event: "turn"; + /** @description The event ID. */ + id?: number; + /** @description The retry time in milliseconds. */ + retry?: number; + })[]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "submit-session": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SessionSubmitInputBody"]; + }; + }; + responses: { + /** @description Accepted */ + 202: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionSubmitOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-session-by-id-suspend": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-session-by-id-transcript": { + parameters: { + query?: { + /** @description Number of recent compaction segments to return. This API parameter keeps compaction-segment semantics even though gc session logs --tail counts displayed transcript entries. Omit for the endpoint default (usually 1); 0 returns all segments; N>0 returns the last N. */ + tail?: string; + /** @description Transcript format: conversation (default) or raw. */ + format?: string; + /** @description Pagination cursor: return entries before this UUID. */ + before?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionTranscriptGetResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-session-by-id-wake": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["OKWithIDResponseBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-sessions": { + parameters: { + query?: { + /** @description Pagination cursor from a previous response's next_cursor field. */ + cursor?: string; + /** @description Maximum number of results to return. 0 = server default. */ + limit?: number; + /** @description Filter by session state (e.g. active, closed). */ + state?: string; + /** @description Filter by session template (agent qualified name). */ + template?: string; + /** @description Include last output preview. */ + peek?: boolean; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ListBodySessionResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "create-session": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SessionCreateBody"]; + }; + }; + responses: { + /** @description Accepted */ + 202: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-sling": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SlingInputBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SlingResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-status": { + parameters: { + query?: { + /** @description Event sequence number; when provided, blocks until a newer event arrives. */ + index?: string; + /** @description How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. */ + wait?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["StatusBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "post-v0-city-by-city-name-unregister": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description Supervisor-registered city name. */ + cityName: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Accepted */ + 202: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CityUnregisterResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-city-by-city-name-workflow-by-workflow-id": { + parameters: { + query?: { + /** @description Scope kind (city or rig). */ + scope_kind?: string; + /** @description Scope reference. */ + scope_ref?: string; + }; + header?: never; + path: { + /** @description City name. */ + cityName: string; + /** @description Workflow (convoy) ID. */ + workflow_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["WorkflowSnapshotResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "delete-v0-city-by-city-name-workflow-by-workflow-id": { + parameters: { + query?: { + /** @description Scope kind (city or rig). */ + scope_kind?: string; + /** @description Scope reference. */ + scope_ref?: string; + /** @description Permanently delete beads from store. */ + delete?: boolean; + }; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Workflow (convoy) ID. */ + workflow_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["WorkflowDeleteResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-events": { + parameters: { + query?: { + /** @description Filter by event type. */ + type?: string; + /** @description Filter by actor. */ + actor?: string; + /** @description Filter to events within the last Go duration (e.g. "5m"). */ + since?: string; + /** @description Maximum number of trailing events to return. 0 = no limit. Used by 'gc events --seq' to compute the head cursor cheaply. */ + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SupervisorEventListOutputBody"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "stream-supervisor-events": { + parameters: { + query?: { + /** @description Alternative to Last-Event-ID for browsers that can't set custom headers. */ + after_cursor?: string; + }; + header?: { + /** @description Reconnect cursor (composite per-city cursor). */ + "Last-Event-ID"?: string; + }; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "text/event-stream": ({ + data: components["schemas"]["HeartbeatEvent"]; + /** + * @description The event name. + * @constant + */ + event: "heartbeat"; + /** @description The event ID (composite cursor). */ + id?: string; + /** @description The retry time in milliseconds. */ + retry?: number; + } | { + data: components["schemas"]["TypedTaggedEventStreamEnvelope"]; + /** + * @description The event name. + * @constant + */ + event: "tagged_event"; + /** @description The event ID (composite cursor). */ + id?: string; + /** @description The retry time in milliseconds. */ + retry?: number; + })[]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-provider-readiness": { + parameters: { + query?: { + /** @description Comma-separated list of providers to probe. */ + providers?: string; + /** @description Force fresh probe, bypassing cache. */ + fresh?: boolean; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProviderReadinessResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; + "get-v0-readiness": { + parameters: { + query?: { + /** @description Comma-separated list of readiness items to check. */ + items?: string; + /** @description Force fresh probe, bypassing cache. */ + fresh?: boolean; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ReadinessResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; +} diff --git a/cmd/gc/dashboard/web/src/generated/sdk.gen.ts b/cmd/gc/dashboard/web/src/generated/sdk.gen.ts new file mode 100644 index 0000000000..be654f696f --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/sdk.gen.ts @@ -0,0 +1,1022 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Client, Options as Options2, TDataShape } from './client'; +import { client } from './client.gen'; +import type { CreateAgentData, CreateAgentErrors, CreateAgentResponses, CreateBeadData, CreateBeadErrors, CreateBeadResponses, CreateConvoyData, CreateConvoyErrors, CreateConvoyResponses, CreateProviderData, CreateProviderErrors, CreateProviderResponses, CreateRigData, CreateRigErrors, CreateRigResponses, CreateSessionData, CreateSessionErrors, CreateSessionResponses, DeleteV0CityByCityNameAgentByBaseData, DeleteV0CityByCityNameAgentByBaseErrors, DeleteV0CityByCityNameAgentByBaseResponses, DeleteV0CityByCityNameAgentByDirByBaseData, DeleteV0CityByCityNameAgentByDirByBaseErrors, DeleteV0CityByCityNameAgentByDirByBaseResponses, DeleteV0CityByCityNameBeadByIdData, DeleteV0CityByCityNameBeadByIdErrors, DeleteV0CityByCityNameBeadByIdResponses, DeleteV0CityByCityNameConvoyByIdData, DeleteV0CityByCityNameConvoyByIdErrors, DeleteV0CityByCityNameConvoyByIdResponses, DeleteV0CityByCityNameExtmsgAdaptersData, DeleteV0CityByCityNameExtmsgAdaptersErrors, DeleteV0CityByCityNameExtmsgAdaptersResponses, DeleteV0CityByCityNameExtmsgParticipantsData, DeleteV0CityByCityNameExtmsgParticipantsErrors, DeleteV0CityByCityNameExtmsgParticipantsResponses, DeleteV0CityByCityNameMailByIdData, DeleteV0CityByCityNameMailByIdErrors, DeleteV0CityByCityNameMailByIdResponses, DeleteV0CityByCityNamePatchesAgentByBaseData, DeleteV0CityByCityNamePatchesAgentByBaseErrors, DeleteV0CityByCityNamePatchesAgentByBaseResponses, DeleteV0CityByCityNamePatchesAgentByDirByBaseData, DeleteV0CityByCityNamePatchesAgentByDirByBaseErrors, DeleteV0CityByCityNamePatchesAgentByDirByBaseResponses, DeleteV0CityByCityNamePatchesProviderByNameData, DeleteV0CityByCityNamePatchesProviderByNameErrors, DeleteV0CityByCityNamePatchesProviderByNameResponses, DeleteV0CityByCityNamePatchesRigByNameData, DeleteV0CityByCityNamePatchesRigByNameErrors, DeleteV0CityByCityNamePatchesRigByNameResponses, DeleteV0CityByCityNameProviderByNameData, DeleteV0CityByCityNameProviderByNameErrors, DeleteV0CityByCityNameProviderByNameResponses, DeleteV0CityByCityNameRigByNameData, DeleteV0CityByCityNameRigByNameErrors, DeleteV0CityByCityNameRigByNameResponses, DeleteV0CityByCityNameWorkflowByWorkflowIdData, DeleteV0CityByCityNameWorkflowByWorkflowIdErrors, DeleteV0CityByCityNameWorkflowByWorkflowIdResponses, EmitEventData, EmitEventErrors, EmitEventResponses, EnsureExtmsgGroupData, EnsureExtmsgGroupErrors, EnsureExtmsgGroupResponses, GetHealthData, GetHealthErrors, GetHealthResponses, GetV0CitiesData, GetV0CitiesErrors, GetV0CitiesResponses, GetV0CityByCityNameAgentByBaseData, GetV0CityByCityNameAgentByBaseErrors, GetV0CityByCityNameAgentByBaseOutputData, GetV0CityByCityNameAgentByBaseOutputErrors, GetV0CityByCityNameAgentByBaseOutputResponses, GetV0CityByCityNameAgentByBaseResponses, GetV0CityByCityNameAgentByDirByBaseData, GetV0CityByCityNameAgentByDirByBaseErrors, GetV0CityByCityNameAgentByDirByBaseOutputData, GetV0CityByCityNameAgentByDirByBaseOutputErrors, GetV0CityByCityNameAgentByDirByBaseOutputResponses, GetV0CityByCityNameAgentByDirByBaseResponses, GetV0CityByCityNameAgentsData, GetV0CityByCityNameAgentsErrors, GetV0CityByCityNameAgentsResponses, GetV0CityByCityNameBeadByIdData, GetV0CityByCityNameBeadByIdDepsData, GetV0CityByCityNameBeadByIdDepsErrors, GetV0CityByCityNameBeadByIdDepsResponses, GetV0CityByCityNameBeadByIdErrors, GetV0CityByCityNameBeadByIdResponses, GetV0CityByCityNameBeadsData, GetV0CityByCityNameBeadsErrors, GetV0CityByCityNameBeadsGraphByRootIdData, GetV0CityByCityNameBeadsGraphByRootIdErrors, GetV0CityByCityNameBeadsGraphByRootIdResponses, GetV0CityByCityNameBeadsReadyData, GetV0CityByCityNameBeadsReadyErrors, GetV0CityByCityNameBeadsReadyResponses, GetV0CityByCityNameBeadsResponses, GetV0CityByCityNameConfigData, GetV0CityByCityNameConfigErrors, GetV0CityByCityNameConfigExplainData, GetV0CityByCityNameConfigExplainErrors, GetV0CityByCityNameConfigExplainResponses, GetV0CityByCityNameConfigResponses, GetV0CityByCityNameConfigValidateData, GetV0CityByCityNameConfigValidateErrors, GetV0CityByCityNameConfigValidateResponses, GetV0CityByCityNameConvoyByIdCheckData, GetV0CityByCityNameConvoyByIdCheckErrors, GetV0CityByCityNameConvoyByIdCheckResponses, GetV0CityByCityNameConvoyByIdData, GetV0CityByCityNameConvoyByIdErrors, GetV0CityByCityNameConvoyByIdResponses, GetV0CityByCityNameConvoysData, GetV0CityByCityNameConvoysErrors, GetV0CityByCityNameConvoysResponses, GetV0CityByCityNameData, GetV0CityByCityNameErrors, GetV0CityByCityNameEventsData, GetV0CityByCityNameEventsErrors, GetV0CityByCityNameEventsResponses, GetV0CityByCityNameExtmsgAdaptersData, GetV0CityByCityNameExtmsgAdaptersErrors, GetV0CityByCityNameExtmsgAdaptersResponses, GetV0CityByCityNameExtmsgBindingsData, GetV0CityByCityNameExtmsgBindingsErrors, GetV0CityByCityNameExtmsgBindingsResponses, GetV0CityByCityNameExtmsgGroupsData, GetV0CityByCityNameExtmsgGroupsErrors, GetV0CityByCityNameExtmsgGroupsResponses, GetV0CityByCityNameExtmsgTranscriptData, GetV0CityByCityNameExtmsgTranscriptErrors, GetV0CityByCityNameExtmsgTranscriptResponses, GetV0CityByCityNameFormulaByNameData, GetV0CityByCityNameFormulaByNameErrors, GetV0CityByCityNameFormulaByNameResponses, GetV0CityByCityNameFormulasByNameData, GetV0CityByCityNameFormulasByNameErrors, GetV0CityByCityNameFormulasByNameResponses, GetV0CityByCityNameFormulasByNameRunsData, GetV0CityByCityNameFormulasByNameRunsErrors, GetV0CityByCityNameFormulasByNameRunsResponses, GetV0CityByCityNameFormulasData, GetV0CityByCityNameFormulasErrors, GetV0CityByCityNameFormulasFeedData, GetV0CityByCityNameFormulasFeedErrors, GetV0CityByCityNameFormulasFeedResponses, GetV0CityByCityNameFormulasResponses, GetV0CityByCityNameHealthData, GetV0CityByCityNameHealthErrors, GetV0CityByCityNameHealthResponses, GetV0CityByCityNameMailByIdData, GetV0CityByCityNameMailByIdErrors, GetV0CityByCityNameMailByIdResponses, GetV0CityByCityNameMailCountData, GetV0CityByCityNameMailCountErrors, GetV0CityByCityNameMailCountResponses, GetV0CityByCityNameMailData, GetV0CityByCityNameMailErrors, GetV0CityByCityNameMailResponses, GetV0CityByCityNameMailThreadByIdData, GetV0CityByCityNameMailThreadByIdErrors, GetV0CityByCityNameMailThreadByIdResponses, GetV0CityByCityNameOrderByNameData, GetV0CityByCityNameOrderByNameErrors, GetV0CityByCityNameOrderByNameResponses, GetV0CityByCityNameOrderHistoryByBeadIdData, GetV0CityByCityNameOrderHistoryByBeadIdErrors, GetV0CityByCityNameOrderHistoryByBeadIdResponses, GetV0CityByCityNameOrdersCheckData, GetV0CityByCityNameOrdersCheckErrors, GetV0CityByCityNameOrdersCheckResponses, GetV0CityByCityNameOrdersData, GetV0CityByCityNameOrdersErrors, GetV0CityByCityNameOrdersFeedData, GetV0CityByCityNameOrdersFeedErrors, GetV0CityByCityNameOrdersFeedResponses, GetV0CityByCityNameOrdersHistoryData, GetV0CityByCityNameOrdersHistoryErrors, GetV0CityByCityNameOrdersHistoryResponses, GetV0CityByCityNameOrdersResponses, GetV0CityByCityNamePacksData, GetV0CityByCityNamePacksErrors, GetV0CityByCityNamePacksResponses, GetV0CityByCityNamePatchesAgentByBaseData, GetV0CityByCityNamePatchesAgentByBaseErrors, GetV0CityByCityNamePatchesAgentByBaseResponses, GetV0CityByCityNamePatchesAgentByDirByBaseData, GetV0CityByCityNamePatchesAgentByDirByBaseErrors, GetV0CityByCityNamePatchesAgentByDirByBaseResponses, GetV0CityByCityNamePatchesAgentsData, GetV0CityByCityNamePatchesAgentsErrors, GetV0CityByCityNamePatchesAgentsResponses, GetV0CityByCityNamePatchesProviderByNameData, GetV0CityByCityNamePatchesProviderByNameErrors, GetV0CityByCityNamePatchesProviderByNameResponses, GetV0CityByCityNamePatchesProvidersData, GetV0CityByCityNamePatchesProvidersErrors, GetV0CityByCityNamePatchesProvidersResponses, GetV0CityByCityNamePatchesRigByNameData, GetV0CityByCityNamePatchesRigByNameErrors, GetV0CityByCityNamePatchesRigByNameResponses, GetV0CityByCityNamePatchesRigsData, GetV0CityByCityNamePatchesRigsErrors, GetV0CityByCityNamePatchesRigsResponses, GetV0CityByCityNameProviderByNameData, GetV0CityByCityNameProviderByNameErrors, GetV0CityByCityNameProviderByNameResponses, GetV0CityByCityNameProviderReadinessData, GetV0CityByCityNameProviderReadinessErrors, GetV0CityByCityNameProviderReadinessResponses, GetV0CityByCityNameProvidersData, GetV0CityByCityNameProvidersErrors, GetV0CityByCityNameProvidersPublicData, GetV0CityByCityNameProvidersPublicErrors, GetV0CityByCityNameProvidersPublicResponses, GetV0CityByCityNameProvidersResponses, GetV0CityByCityNameReadinessData, GetV0CityByCityNameReadinessErrors, GetV0CityByCityNameReadinessResponses, GetV0CityByCityNameResponses, GetV0CityByCityNameRigByNameData, GetV0CityByCityNameRigByNameErrors, GetV0CityByCityNameRigByNameResponses, GetV0CityByCityNameRigsData, GetV0CityByCityNameRigsErrors, GetV0CityByCityNameRigsResponses, GetV0CityByCityNameServiceByNameData, GetV0CityByCityNameServiceByNameErrors, GetV0CityByCityNameServiceByNameResponses, GetV0CityByCityNameServicesData, GetV0CityByCityNameServicesErrors, GetV0CityByCityNameServicesResponses, GetV0CityByCityNameSessionByIdAgentsByAgentIdData, GetV0CityByCityNameSessionByIdAgentsByAgentIdErrors, GetV0CityByCityNameSessionByIdAgentsByAgentIdResponses, GetV0CityByCityNameSessionByIdAgentsData, GetV0CityByCityNameSessionByIdAgentsErrors, GetV0CityByCityNameSessionByIdAgentsResponses, GetV0CityByCityNameSessionByIdData, GetV0CityByCityNameSessionByIdErrors, GetV0CityByCityNameSessionByIdPendingData, GetV0CityByCityNameSessionByIdPendingErrors, GetV0CityByCityNameSessionByIdPendingResponses, GetV0CityByCityNameSessionByIdResponses, GetV0CityByCityNameSessionByIdTranscriptData, GetV0CityByCityNameSessionByIdTranscriptErrors, GetV0CityByCityNameSessionByIdTranscriptResponses, GetV0CityByCityNameSessionsData, GetV0CityByCityNameSessionsErrors, GetV0CityByCityNameSessionsResponses, GetV0CityByCityNameStatusData, GetV0CityByCityNameStatusErrors, GetV0CityByCityNameStatusResponses, GetV0CityByCityNameWorkflowByWorkflowIdData, GetV0CityByCityNameWorkflowByWorkflowIdErrors, GetV0CityByCityNameWorkflowByWorkflowIdResponses, GetV0EventsData, GetV0EventsErrors, GetV0EventsResponses, GetV0ProviderReadinessData, GetV0ProviderReadinessErrors, GetV0ProviderReadinessResponses, GetV0ReadinessData, GetV0ReadinessErrors, GetV0ReadinessResponses, PatchV0CityByCityNameAgentByBaseData, PatchV0CityByCityNameAgentByBaseErrors, PatchV0CityByCityNameAgentByBaseResponses, PatchV0CityByCityNameAgentByDirByBaseData, PatchV0CityByCityNameAgentByDirByBaseErrors, PatchV0CityByCityNameAgentByDirByBaseResponses, PatchV0CityByCityNameBeadByIdData, PatchV0CityByCityNameBeadByIdErrors, PatchV0CityByCityNameBeadByIdResponses, PatchV0CityByCityNameData, PatchV0CityByCityNameErrors, PatchV0CityByCityNameProviderByNameData, PatchV0CityByCityNameProviderByNameErrors, PatchV0CityByCityNameProviderByNameResponses, PatchV0CityByCityNameResponses, PatchV0CityByCityNameRigByNameData, PatchV0CityByCityNameRigByNameErrors, PatchV0CityByCityNameRigByNameResponses, PatchV0CityByCityNameSessionByIdData, PatchV0CityByCityNameSessionByIdErrors, PatchV0CityByCityNameSessionByIdResponses, PostV0CityByCityNameAgentByBaseByActionData, PostV0CityByCityNameAgentByBaseByActionErrors, PostV0CityByCityNameAgentByBaseByActionResponses, PostV0CityByCityNameAgentByDirByBaseByActionData, PostV0CityByCityNameAgentByDirByBaseByActionErrors, PostV0CityByCityNameAgentByDirByBaseByActionResponses, PostV0CityByCityNameBeadByIdAssignData, PostV0CityByCityNameBeadByIdAssignErrors, PostV0CityByCityNameBeadByIdAssignResponses, PostV0CityByCityNameBeadByIdCloseData, PostV0CityByCityNameBeadByIdCloseErrors, PostV0CityByCityNameBeadByIdCloseResponses, PostV0CityByCityNameBeadByIdReopenData, PostV0CityByCityNameBeadByIdReopenErrors, PostV0CityByCityNameBeadByIdReopenResponses, PostV0CityByCityNameBeadByIdUpdateData, PostV0CityByCityNameBeadByIdUpdateErrors, PostV0CityByCityNameBeadByIdUpdateResponses, PostV0CityByCityNameConvoyByIdAddData, PostV0CityByCityNameConvoyByIdAddErrors, PostV0CityByCityNameConvoyByIdAddResponses, PostV0CityByCityNameConvoyByIdCloseData, PostV0CityByCityNameConvoyByIdCloseErrors, PostV0CityByCityNameConvoyByIdCloseResponses, PostV0CityByCityNameConvoyByIdRemoveData, PostV0CityByCityNameConvoyByIdRemoveErrors, PostV0CityByCityNameConvoyByIdRemoveResponses, PostV0CityByCityNameExtmsgBindData, PostV0CityByCityNameExtmsgBindErrors, PostV0CityByCityNameExtmsgBindResponses, PostV0CityByCityNameExtmsgInboundData, PostV0CityByCityNameExtmsgInboundErrors, PostV0CityByCityNameExtmsgInboundResponses, PostV0CityByCityNameExtmsgOutboundData, PostV0CityByCityNameExtmsgOutboundErrors, PostV0CityByCityNameExtmsgOutboundResponses, PostV0CityByCityNameExtmsgParticipantsData, PostV0CityByCityNameExtmsgParticipantsErrors, PostV0CityByCityNameExtmsgParticipantsResponses, PostV0CityByCityNameExtmsgTranscriptAckData, PostV0CityByCityNameExtmsgTranscriptAckErrors, PostV0CityByCityNameExtmsgTranscriptAckResponses, PostV0CityByCityNameExtmsgUnbindData, PostV0CityByCityNameExtmsgUnbindErrors, PostV0CityByCityNameExtmsgUnbindResponses, PostV0CityByCityNameFormulasByNamePreviewData, PostV0CityByCityNameFormulasByNamePreviewErrors, PostV0CityByCityNameFormulasByNamePreviewResponses, PostV0CityByCityNameMailByIdArchiveData, PostV0CityByCityNameMailByIdArchiveErrors, PostV0CityByCityNameMailByIdArchiveResponses, PostV0CityByCityNameMailByIdMarkUnreadData, PostV0CityByCityNameMailByIdMarkUnreadErrors, PostV0CityByCityNameMailByIdMarkUnreadResponses, PostV0CityByCityNameMailByIdReadData, PostV0CityByCityNameMailByIdReadErrors, PostV0CityByCityNameMailByIdReadResponses, PostV0CityByCityNameOrderByNameDisableData, PostV0CityByCityNameOrderByNameDisableErrors, PostV0CityByCityNameOrderByNameDisableResponses, PostV0CityByCityNameOrderByNameEnableData, PostV0CityByCityNameOrderByNameEnableErrors, PostV0CityByCityNameOrderByNameEnableResponses, PostV0CityByCityNameRigByNameByActionData, PostV0CityByCityNameRigByNameByActionErrors, PostV0CityByCityNameRigByNameByActionResponses, PostV0CityByCityNameServiceByNameRestartData, PostV0CityByCityNameServiceByNameRestartErrors, PostV0CityByCityNameServiceByNameRestartResponses, PostV0CityByCityNameSessionByIdCloseData, PostV0CityByCityNameSessionByIdCloseErrors, PostV0CityByCityNameSessionByIdCloseResponses, PostV0CityByCityNameSessionByIdKillData, PostV0CityByCityNameSessionByIdKillErrors, PostV0CityByCityNameSessionByIdKillResponses, PostV0CityByCityNameSessionByIdRenameData, PostV0CityByCityNameSessionByIdRenameErrors, PostV0CityByCityNameSessionByIdRenameResponses, PostV0CityByCityNameSessionByIdStopData, PostV0CityByCityNameSessionByIdStopErrors, PostV0CityByCityNameSessionByIdStopResponses, PostV0CityByCityNameSessionByIdSuspendData, PostV0CityByCityNameSessionByIdSuspendErrors, PostV0CityByCityNameSessionByIdSuspendResponses, PostV0CityByCityNameSessionByIdWakeData, PostV0CityByCityNameSessionByIdWakeErrors, PostV0CityByCityNameSessionByIdWakeResponses, PostV0CityByCityNameSlingData, PostV0CityByCityNameSlingErrors, PostV0CityByCityNameSlingResponses, PostV0CityByCityNameUnregisterData, PostV0CityByCityNameUnregisterErrors, PostV0CityByCityNameUnregisterResponses, PostV0CityData, PostV0CityErrors, PostV0CityResponses, PutV0CityByCityNamePatchesAgentsData, PutV0CityByCityNamePatchesAgentsErrors, PutV0CityByCityNamePatchesAgentsResponses, PutV0CityByCityNamePatchesProvidersData, PutV0CityByCityNamePatchesProvidersErrors, PutV0CityByCityNamePatchesProvidersResponses, PutV0CityByCityNamePatchesRigsData, PutV0CityByCityNamePatchesRigsErrors, PutV0CityByCityNamePatchesRigsResponses, RegisterExtmsgAdapterData, RegisterExtmsgAdapterErrors, RegisterExtmsgAdapterResponses, ReplyMailData, ReplyMailErrors, ReplyMailResponses, RespondSessionData, RespondSessionErrors, RespondSessionResponses, SendMailData, SendMailErrors, SendMailResponses, SendSessionMessageData, SendSessionMessageErrors, SendSessionMessageResponses, StreamAgentOutputData, StreamAgentOutputErrors, StreamAgentOutputQualifiedData, StreamAgentOutputQualifiedErrors, StreamAgentOutputQualifiedResponse, StreamAgentOutputQualifiedResponses, StreamAgentOutputResponse, StreamAgentOutputResponses, StreamEventsData, StreamEventsErrors, StreamEventsResponse, StreamEventsResponses, StreamSessionData, StreamSessionErrors, StreamSessionResponse, StreamSessionResponses, StreamSupervisorEventsData, StreamSupervisorEventsErrors, StreamSupervisorEventsResponse, StreamSupervisorEventsResponses, SubmitSessionData, SubmitSessionErrors, SubmitSessionResponses } from './types.gen'; + +export type Options = Options2 & { + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client; + /** + * You can pass arbitrary values through the `meta` object. This can be + * used to access values that aren't defined as part of the SDK function. + */ + meta?: Record; +}; + +/** + * Get health + */ +export const getHealth = (options?: Options) => (options?.client ?? client).get({ url: '/health', ...options }); + +/** + * Get v0 cities + */ +export const getV0Cities = (options?: Options) => (options?.client ?? client).get({ url: '/v0/cities', ...options }); + +/** + * Post v0 city + */ +export const postV0City = (options: Options) => (options.client ?? client).post({ + url: '/v0/city', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name + */ +export const getV0CityByCityName = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}', ...options }); + +/** + * Patch v0 city by city name + */ +export const patchV0CityByCityName = (options: Options) => (options.client ?? client).patch({ + url: '/v0/city/{cityName}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Delete v0 city by city name agent by base + */ +export const deleteV0CityByCityNameAgentByBase = (options: Options) => (options.client ?? client).delete({ url: '/v0/city/{cityName}/agent/{base}', ...options }); + +/** + * Get v0 city by city name agent by base + */ +export const getV0CityByCityNameAgentByBase = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/agent/{base}', ...options }); + +/** + * Patch v0 city by city name agent by base + */ +export const patchV0CityByCityNameAgentByBase = (options: Options) => (options.client ?? client).patch({ + url: '/v0/city/{cityName}/agent/{base}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name agent by base output + */ +export const getV0CityByCityNameAgentByBaseOutput = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/agent/{base}/output', ...options }); + +/** + * Stream agent output in real time + * + * Server-Sent Events stream of agent output (session log tail or tmux pane polling). + */ +export const streamAgentOutput = (options: Options) => (options.client ?? client).sse.get({ url: '/v0/city/{cityName}/agent/{base}/output/stream', ...options }); + +/** + * Post v0 city by city name agent by base by action + */ +export const postV0CityByCityNameAgentByBaseByAction = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/agent/{base}/{action}', ...options }); + +/** + * Delete v0 city by city name agent by dir by base + */ +export const deleteV0CityByCityNameAgentByDirByBase = (options: Options) => (options.client ?? client).delete({ url: '/v0/city/{cityName}/agent/{dir}/{base}', ...options }); + +/** + * Get v0 city by city name agent by dir by base + */ +export const getV0CityByCityNameAgentByDirByBase = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/agent/{dir}/{base}', ...options }); + +/** + * Patch v0 city by city name agent by dir by base + */ +export const patchV0CityByCityNameAgentByDirByBase = (options: Options) => (options.client ?? client).patch({ + url: '/v0/city/{cityName}/agent/{dir}/{base}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name agent by dir by base output + */ +export const getV0CityByCityNameAgentByDirByBaseOutput = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/agent/{dir}/{base}/output', ...options }); + +/** + * Stream agent output in real time (qualified name) + * + * Server-Sent Events stream of agent output for qualified (rig-prefixed) agent names. + */ +export const streamAgentOutputQualified = (options: Options) => (options.client ?? client).sse.get({ url: '/v0/city/{cityName}/agent/{dir}/{base}/output/stream', ...options }); + +/** + * Post v0 city by city name agent by dir by base by action + */ +export const postV0CityByCityNameAgentByDirByBaseByAction = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/agent/{dir}/{base}/{action}', ...options }); + +/** + * Get v0 city by city name agents + */ +export const getV0CityByCityNameAgents = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/agents', ...options }); + +/** + * Create an agent + */ +export const createAgent = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/agents', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Delete v0 city by city name bead by ID + */ +export const deleteV0CityByCityNameBeadById = (options: Options) => (options.client ?? client).delete({ url: '/v0/city/{cityName}/bead/{id}', ...options }); + +/** + * Get v0 city by city name bead by ID + */ +export const getV0CityByCityNameBeadById = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/bead/{id}', ...options }); + +/** + * Patch v0 city by city name bead by ID + */ +export const patchV0CityByCityNameBeadById = (options: Options) => (options.client ?? client).patch({ + url: '/v0/city/{cityName}/bead/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Post v0 city by city name bead by ID assign + */ +export const postV0CityByCityNameBeadByIdAssign = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/bead/{id}/assign', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Post v0 city by city name bead by ID close + */ +export const postV0CityByCityNameBeadByIdClose = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/bead/{id}/close', ...options }); + +/** + * Get v0 city by city name bead by ID deps + */ +export const getV0CityByCityNameBeadByIdDeps = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/bead/{id}/deps', ...options }); + +/** + * Post v0 city by city name bead by ID reopen + */ +export const postV0CityByCityNameBeadByIdReopen = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/bead/{id}/reopen', ...options }); + +/** + * Post v0 city by city name bead by ID update + */ +export const postV0CityByCityNameBeadByIdUpdate = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/bead/{id}/update', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name beads + */ +export const getV0CityByCityNameBeads = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/beads', ...options }); + +/** + * Create a bead + */ +export const createBead = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/beads', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name beads graph by root ID + */ +export const getV0CityByCityNameBeadsGraphByRootId = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/beads/graph/{rootID}', ...options }); + +/** + * Get v0 city by city name beads ready + */ +export const getV0CityByCityNameBeadsReady = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/beads/ready', ...options }); + +/** + * Get v0 city by city name config + */ +export const getV0CityByCityNameConfig = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/config', ...options }); + +/** + * Get v0 city by city name config explain + */ +export const getV0CityByCityNameConfigExplain = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/config/explain', ...options }); + +/** + * Get v0 city by city name config validate + */ +export const getV0CityByCityNameConfigValidate = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/config/validate', ...options }); + +/** + * Delete v0 city by city name convoy by ID + */ +export const deleteV0CityByCityNameConvoyById = (options: Options) => (options.client ?? client).delete({ url: '/v0/city/{cityName}/convoy/{id}', ...options }); + +/** + * Get v0 city by city name convoy by ID + */ +export const getV0CityByCityNameConvoyById = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/convoy/{id}', ...options }); + +/** + * Post v0 city by city name convoy by ID add + */ +export const postV0CityByCityNameConvoyByIdAdd = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/convoy/{id}/add', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name convoy by ID check + */ +export const getV0CityByCityNameConvoyByIdCheck = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/convoy/{id}/check', ...options }); + +/** + * Post v0 city by city name convoy by ID close + */ +export const postV0CityByCityNameConvoyByIdClose = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/convoy/{id}/close', ...options }); + +/** + * Post v0 city by city name convoy by ID remove + */ +export const postV0CityByCityNameConvoyByIdRemove = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/convoy/{id}/remove', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name convoys + */ +export const getV0CityByCityNameConvoys = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/convoys', ...options }); + +/** + * Create a convoy + */ +export const createConvoy = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/convoys', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name events + */ +export const getV0CityByCityNameEvents = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/events', ...options }); + +/** + * Emit an event + */ +export const emitEvent = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/events', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Stream city events in real time + * + * Server-Sent Events stream of city events with optional workflow projections. Supports reconnection via Last-Event-ID header or after_seq query param. + */ +export const streamEvents = (options: Options) => (options.client ?? client).sse.get({ url: '/v0/city/{cityName}/events/stream', ...options }); + +/** + * Delete v0 city by city name extmsg adapters + */ +export const deleteV0CityByCityNameExtmsgAdapters = (options: Options) => (options.client ?? client).delete({ + url: '/v0/city/{cityName}/extmsg/adapters', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name extmsg adapters + */ +export const getV0CityByCityNameExtmsgAdapters = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/extmsg/adapters', ...options }); + +/** + * Register an external messaging adapter + */ +export const registerExtmsgAdapter = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/extmsg/adapters', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Post v0 city by city name extmsg bind + */ +export const postV0CityByCityNameExtmsgBind = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/extmsg/bind', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name extmsg bindings + */ +export const getV0CityByCityNameExtmsgBindings = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/extmsg/bindings', ...options }); + +/** + * Get v0 city by city name extmsg groups + */ +export const getV0CityByCityNameExtmsgGroups = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/extmsg/groups', ...options }); + +/** + * Ensure an external messaging group exists + */ +export const ensureExtmsgGroup = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/extmsg/groups', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Post v0 city by city name extmsg inbound + */ +export const postV0CityByCityNameExtmsgInbound = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/extmsg/inbound', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Post v0 city by city name extmsg outbound + */ +export const postV0CityByCityNameExtmsgOutbound = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/extmsg/outbound', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Delete v0 city by city name extmsg participants + */ +export const deleteV0CityByCityNameExtmsgParticipants = (options: Options) => (options.client ?? client).delete({ + url: '/v0/city/{cityName}/extmsg/participants', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Post v0 city by city name extmsg participants + */ +export const postV0CityByCityNameExtmsgParticipants = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/extmsg/participants', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name extmsg transcript + */ +export const getV0CityByCityNameExtmsgTranscript = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/extmsg/transcript', ...options }); + +/** + * Post v0 city by city name extmsg transcript ack + */ +export const postV0CityByCityNameExtmsgTranscriptAck = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/extmsg/transcript/ack', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Post v0 city by city name extmsg unbind + */ +export const postV0CityByCityNameExtmsgUnbind = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/extmsg/unbind', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name formula by name + */ +export const getV0CityByCityNameFormulaByName = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/formula/{name}', ...options }); + +/** + * Get v0 city by city name formulas + */ +export const getV0CityByCityNameFormulas = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/formulas', ...options }); + +/** + * Get v0 city by city name formulas feed + */ +export const getV0CityByCityNameFormulasFeed = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/formulas/feed', ...options }); + +/** + * Get v0 city by city name formulas by name + */ +export const getV0CityByCityNameFormulasByName = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/formulas/{name}', ...options }); + +/** + * Post v0 city by city name formulas by name preview + */ +export const postV0CityByCityNameFormulasByNamePreview = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/formulas/{name}/preview', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name formulas by name runs + */ +export const getV0CityByCityNameFormulasByNameRuns = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/formulas/{name}/runs', ...options }); + +/** + * Get v0 city by city name health + */ +export const getV0CityByCityNameHealth = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/health', ...options }); + +/** + * Get v0 city by city name mail + */ +export const getV0CityByCityNameMail = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/mail', ...options }); + +/** + * Send a mail message + */ +export const sendMail = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/mail', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name mail count + */ +export const getV0CityByCityNameMailCount = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/mail/count', ...options }); + +/** + * Get v0 city by city name mail thread by ID + */ +export const getV0CityByCityNameMailThreadById = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/mail/thread/{id}', ...options }); + +/** + * Delete v0 city by city name mail by ID + */ +export const deleteV0CityByCityNameMailById = (options: Options) => (options.client ?? client).delete({ url: '/v0/city/{cityName}/mail/{id}', ...options }); + +/** + * Get v0 city by city name mail by ID + */ +export const getV0CityByCityNameMailById = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/mail/{id}', ...options }); + +/** + * Post v0 city by city name mail by ID archive + */ +export const postV0CityByCityNameMailByIdArchive = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/mail/{id}/archive', ...options }); + +/** + * Post v0 city by city name mail by ID mark unread + */ +export const postV0CityByCityNameMailByIdMarkUnread = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/mail/{id}/mark-unread', ...options }); + +/** + * Post v0 city by city name mail by ID read + */ +export const postV0CityByCityNameMailByIdRead = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/mail/{id}/read', ...options }); + +/** + * Reply to a mail message + */ +export const replyMail = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/mail/{id}/reply', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name order history by bead ID + */ +export const getV0CityByCityNameOrderHistoryByBeadId = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/order/history/{bead_id}', ...options }); + +/** + * Get v0 city by city name order by name + */ +export const getV0CityByCityNameOrderByName = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/order/{name}', ...options }); + +/** + * Post v0 city by city name order by name disable + */ +export const postV0CityByCityNameOrderByNameDisable = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/order/{name}/disable', ...options }); + +/** + * Post v0 city by city name order by name enable + */ +export const postV0CityByCityNameOrderByNameEnable = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/order/{name}/enable', ...options }); + +/** + * Get v0 city by city name orders + */ +export const getV0CityByCityNameOrders = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/orders', ...options }); + +/** + * Get v0 city by city name orders check + */ +export const getV0CityByCityNameOrdersCheck = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/orders/check', ...options }); + +/** + * Get v0 city by city name orders feed + */ +export const getV0CityByCityNameOrdersFeed = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/orders/feed', ...options }); + +/** + * Get v0 city by city name orders history + */ +export const getV0CityByCityNameOrdersHistory = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/orders/history', ...options }); + +/** + * Get v0 city by city name packs + */ +export const getV0CityByCityNamePacks = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/packs', ...options }); + +/** + * Delete v0 city by city name patches agent by base + */ +export const deleteV0CityByCityNamePatchesAgentByBase = (options: Options) => (options.client ?? client).delete({ url: '/v0/city/{cityName}/patches/agent/{base}', ...options }); + +/** + * Get v0 city by city name patches agent by base + */ +export const getV0CityByCityNamePatchesAgentByBase = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/patches/agent/{base}', ...options }); + +/** + * Delete v0 city by city name patches agent by dir by base + */ +export const deleteV0CityByCityNamePatchesAgentByDirByBase = (options: Options) => (options.client ?? client).delete({ url: '/v0/city/{cityName}/patches/agent/{dir}/{base}', ...options }); + +/** + * Get v0 city by city name patches agent by dir by base + */ +export const getV0CityByCityNamePatchesAgentByDirByBase = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/patches/agent/{dir}/{base}', ...options }); + +/** + * Get v0 city by city name patches agents + */ +export const getV0CityByCityNamePatchesAgents = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/patches/agents', ...options }); + +/** + * Put v0 city by city name patches agents + */ +export const putV0CityByCityNamePatchesAgents = (options: Options) => (options.client ?? client).put({ + url: '/v0/city/{cityName}/patches/agents', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Delete v0 city by city name patches provider by name + */ +export const deleteV0CityByCityNamePatchesProviderByName = (options: Options) => (options.client ?? client).delete({ url: '/v0/city/{cityName}/patches/provider/{name}', ...options }); + +/** + * Get v0 city by city name patches provider by name + */ +export const getV0CityByCityNamePatchesProviderByName = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/patches/provider/{name}', ...options }); + +/** + * Get v0 city by city name patches providers + */ +export const getV0CityByCityNamePatchesProviders = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/patches/providers', ...options }); + +/** + * Put v0 city by city name patches providers + */ +export const putV0CityByCityNamePatchesProviders = (options: Options) => (options.client ?? client).put({ + url: '/v0/city/{cityName}/patches/providers', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Delete v0 city by city name patches rig by name + */ +export const deleteV0CityByCityNamePatchesRigByName = (options: Options) => (options.client ?? client).delete({ url: '/v0/city/{cityName}/patches/rig/{name}', ...options }); + +/** + * Get v0 city by city name patches rig by name + */ +export const getV0CityByCityNamePatchesRigByName = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/patches/rig/{name}', ...options }); + +/** + * Get v0 city by city name patches rigs + */ +export const getV0CityByCityNamePatchesRigs = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/patches/rigs', ...options }); + +/** + * Put v0 city by city name patches rigs + */ +export const putV0CityByCityNamePatchesRigs = (options: Options) => (options.client ?? client).put({ + url: '/v0/city/{cityName}/patches/rigs', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name provider readiness + */ +export const getV0CityByCityNameProviderReadiness = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/provider-readiness', ...options }); + +/** + * Delete v0 city by city name provider by name + */ +export const deleteV0CityByCityNameProviderByName = (options: Options) => (options.client ?? client).delete({ url: '/v0/city/{cityName}/provider/{name}', ...options }); + +/** + * Get v0 city by city name provider by name + */ +export const getV0CityByCityNameProviderByName = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/provider/{name}', ...options }); + +/** + * Patch v0 city by city name provider by name + */ +export const patchV0CityByCityNameProviderByName = (options: Options) => (options.client ?? client).patch({ + url: '/v0/city/{cityName}/provider/{name}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name providers + */ +export const getV0CityByCityNameProviders = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/providers', ...options }); + +/** + * Create a provider + */ +export const createProvider = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/providers', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name providers public + */ +export const getV0CityByCityNameProvidersPublic = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/providers/public', ...options }); + +/** + * Get v0 city by city name readiness + */ +export const getV0CityByCityNameReadiness = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/readiness', ...options }); + +/** + * Delete v0 city by city name rig by name + */ +export const deleteV0CityByCityNameRigByName = (options: Options) => (options.client ?? client).delete({ url: '/v0/city/{cityName}/rig/{name}', ...options }); + +/** + * Get v0 city by city name rig by name + */ +export const getV0CityByCityNameRigByName = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/rig/{name}', ...options }); + +/** + * Patch v0 city by city name rig by name + */ +export const patchV0CityByCityNameRigByName = (options: Options) => (options.client ?? client).patch({ + url: '/v0/city/{cityName}/rig/{name}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Post v0 city by city name rig by name by action + */ +export const postV0CityByCityNameRigByNameByAction = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/rig/{name}/{action}', ...options }); + +/** + * Get v0 city by city name rigs + */ +export const getV0CityByCityNameRigs = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/rigs', ...options }); + +/** + * Create a rig + */ +export const createRig = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/rigs', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name service by name + */ +export const getV0CityByCityNameServiceByName = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/service/{name}', ...options }); + +/** + * Post v0 city by city name service by name restart + */ +export const postV0CityByCityNameServiceByNameRestart = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/service/{name}/restart', ...options }); + +/** + * Get v0 city by city name services + */ +export const getV0CityByCityNameServices = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/services', ...options }); + +/** + * Get v0 city by city name session by ID + */ +export const getV0CityByCityNameSessionById = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/session/{id}', ...options }); + +/** + * Patch v0 city by city name session by ID + */ +export const patchV0CityByCityNameSessionById = (options: Options) => (options.client ?? client).patch({ + url: '/v0/city/{cityName}/session/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name session by ID agents + */ +export const getV0CityByCityNameSessionByIdAgents = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/session/{id}/agents', ...options }); + +/** + * Get v0 city by city name session by ID agents by agent ID + */ +export const getV0CityByCityNameSessionByIdAgentsByAgentId = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/session/{id}/agents/{agentId}', ...options }); + +/** + * Post v0 city by city name session by ID close + */ +export const postV0CityByCityNameSessionByIdClose = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/session/{id}/close', ...options }); + +/** + * Post v0 city by city name session by ID kill + */ +export const postV0CityByCityNameSessionByIdKill = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/session/{id}/kill', ...options }); + +/** + * Send a message to a session + */ +export const sendSessionMessage = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/session/{id}/messages', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name session by ID pending + */ +export const getV0CityByCityNameSessionByIdPending = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/session/{id}/pending', ...options }); + +/** + * Post v0 city by city name session by ID rename + */ +export const postV0CityByCityNameSessionByIdRename = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/session/{id}/rename', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Respond to a pending interaction + */ +export const respondSession = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/session/{id}/respond', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Post v0 city by city name session by ID stop + */ +export const postV0CityByCityNameSessionByIdStop = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/session/{id}/stop', ...options }); + +/** + * Stream session output in real time + * + * Server-Sent Events stream of session transcript updates. Streams turns (conversation format) or raw messages (JSONL format) based on the format query parameter. Emits activity and pending events for tool approval prompts. + */ +export const streamSession = (options: Options) => (options.client ?? client).sse.get({ url: '/v0/city/{cityName}/session/{id}/stream', ...options }); + +/** + * Submit a message to a session + */ +export const submitSession = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/session/{id}/submit', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Post v0 city by city name session by ID suspend + */ +export const postV0CityByCityNameSessionByIdSuspend = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/session/{id}/suspend', ...options }); + +/** + * Get v0 city by city name session by ID transcript + */ +export const getV0CityByCityNameSessionByIdTranscript = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/session/{id}/transcript', ...options }); + +/** + * Post v0 city by city name session by ID wake + */ +export const postV0CityByCityNameSessionByIdWake = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/session/{id}/wake', ...options }); + +/** + * Get v0 city by city name sessions + */ +export const getV0CityByCityNameSessions = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/sessions', ...options }); + +/** + * Create a session + */ +export const createSession = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/sessions', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Post v0 city by city name sling + */ +export const postV0CityByCityNameSling = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/sling', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Get v0 city by city name status + */ +export const getV0CityByCityNameStatus = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/status', ...options }); + +/** + * Post v0 city by city name unregister + */ +export const postV0CityByCityNameUnregister = (options: Options) => (options.client ?? client).post({ url: '/v0/city/{cityName}/unregister', ...options }); + +/** + * Delete v0 city by city name workflow by workflow ID + */ +export const deleteV0CityByCityNameWorkflowByWorkflowId = (options: Options) => (options.client ?? client).delete({ url: '/v0/city/{cityName}/workflow/{workflow_id}', ...options }); + +/** + * Get v0 city by city name workflow by workflow ID + */ +export const getV0CityByCityNameWorkflowByWorkflowId = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/workflow/{workflow_id}', ...options }); + +/** + * Get v0 events + */ +export const getV0Events = (options?: Options) => (options?.client ?? client).get({ url: '/v0/events', ...options }); + +/** + * Stream tagged events from all running cities. + */ +export const streamSupervisorEvents = (options?: Options) => (options?.client ?? client).sse.get({ url: '/v0/events/stream', ...options }); + +/** + * Get v0 provider readiness + */ +export const getV0ProviderReadiness = (options?: Options) => (options?.client ?? client).get({ url: '/v0/provider-readiness', ...options }); + +/** + * Get v0 readiness + */ +export const getV0Readiness = (options?: Options) => (options?.client ?? client).get({ url: '/v0/readiness', ...options }); diff --git a/cmd/gc/dashboard/web/src/generated/types.gen.ts b/cmd/gc/dashboard/web/src/generated/types.gen.ts new file mode 100644 index 0000000000..f516c7dcf0 --- /dev/null +++ b/cmd/gc/dashboard/web/src/generated/types.gen.ts @@ -0,0 +1,10069 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: `${string}://${string}` | (string & {}); +}; + +export type AdapterCapabilities = { + MaxMessageLength: number; + SupportsAttachments: boolean; + SupportsChildConversations: boolean; +}; + +export type AdapterEventPayload = { + account_id: string; + provider: string; +}; + +export type AgentCreateInputBody = { + /** + * Working directory (rig name). + */ + dir?: string; + /** + * Agent name. + */ + name: string; + /** + * Provider name. + */ + provider: string; + /** + * Agent scope. + */ + scope?: string; +}; + +export type AgentCreatedOutputBody = { + /** + * Created agent name. + */ + agent: string; + /** + * Operation result. + */ + status: string; +}; + +export type AgentMapping = { + agent_id: string; + parent_tool_use_id: string; +}; + +export type AgentOutputResponse = { + agent: string; + format: string; + pagination?: PaginationInfo; + turns: Array | null; +}; + +export type AgentPatch = { + AppendFragments: Array | null; + Attach: boolean | null; + DefaultSlingFormula: string | null; + DependsOn: Array | null; + Dir: string; + Env: { + [key: string]: string; + }; + EnvRemove: Array | null; + HooksInstalled: boolean | null; + IdleTimeout: string | null; + InjectAssignedSkills: boolean | null; + InjectFragments: Array | null; + InjectFragmentsAppend: Array | null; + InstallAgentHooks: Array | null; + InstallAgentHooksAppend: Array | null; + MCP: Array | null; + MCPAppend: Array | null; + MaxActiveSessions: number | null; + MinActiveSessions: number | null; + Name: string; + Nudge: string | null; + OptionDefaults: { + [key: string]: string; + }; + OverlayDir: string | null; + Pool: PoolOverride; + PreStart: Array | null; + PreStartAppend: Array | null; + PromptTemplate: string | null; + Provider: string | null; + ResumeCommand: string | null; + ScaleCheck: string | null; + Scope: string | null; + Session: string | null; + SessionLive: Array | null; + SessionLiveAppend: Array | null; + SessionSetup: Array | null; + SessionSetupAppend: Array | null; + SessionSetupScript: string | null; + Skills: Array | null; + SkillsAppend: Array | null; + SleepAfterIdle: string | null; + StartCommand: string | null; + Suspended: boolean | null; + WakeMode: string | null; + WorkDir: string | null; +}; + +export type AgentPatchSetInputBody = { + /** + * Agent directory scope. + */ + dir?: string; + /** + * Override environment variables. + */ + env?: { + [key: string]: string; + }; + /** + * Agent name. + */ + name?: string; + /** + * Override agent scope. + */ + scope?: string; + /** + * Override suspended state. + */ + suspended?: boolean; + /** + * Override session working directory. + */ + work_dir?: string; +}; + +export type AgentResponse = { + active_bead?: string; + activity?: string; + available: boolean; + context_pct?: number; + context_window?: number; + description?: string; + display_name?: string; + last_output?: string; + model?: string; + name: string; + pool?: string; + provider?: string; + rig?: string; + running: boolean; + session?: SessionInfo; + state: string; + suspended: boolean; + unavailable_reason?: string; +}; + +export type AgentUpdateInputBody = { + /** + * Provider name. + */ + provider?: string; + /** + * Agent scope. + */ + scope?: string; + /** + * Whether agent is suspended. + */ + suspended?: boolean; +}; + +export type AgentUpdateQualifiedInputBody = { + /** + * Provider name. + */ + provider?: string; + /** + * Agent scope. + */ + scope?: string; + /** + * Whether agent is suspended. + */ + suspended?: boolean; +}; + +export type AnnotatedAgentResponse = { + dir?: string; + is_pool?: boolean; + name: string; + /** + * Agent origin: inline or pack-derived. + */ + origin: string; + provider?: string; + scope?: string; + suspended: boolean; +}; + +export type AnnotatedProviderResponse = { + acp_args?: Array; + acp_command?: string; + args?: Array | null; + command?: string; + display_name?: string; + env?: { + [key: string]: string; + }; + /** + * Provider origin: builtin, city, or builtin+city. + */ + origin: string; + prompt_flag?: string; + prompt_mode?: string; + ready_delay_ms?: number; +}; + +export type Bead = { + assignee?: string; + created_at: string; + dependencies?: Array | null; + description?: string; + from?: string; + id: string; + issue_type: string; + labels?: Array | null; + metadata?: { + [key: string]: string; + }; + needs?: Array | null; + parent?: string; + priority?: number; + ref?: string; + status: string; + title: string; +}; + +export type BeadAssignInputBody = { + /** + * Assignee name. + */ + assignee?: string; +}; + +export type BeadCreateInputBody = { + /** + * Assigned agent. + */ + assignee?: string; + /** + * Bead description. + */ + description?: string; + /** + * Bead labels. + */ + labels?: Array | null; + /** + * Metadata key-value pairs to set at create time. + */ + metadata?: { + [key: string]: string; + }; + /** + * Parent bead ID. + */ + parent?: string; + /** + * Bead priority. + */ + priority?: number; + /** + * Rig name. + */ + rig?: string; + /** + * Bead title. + */ + title: string; + /** + * Bead type. + */ + type?: string; +}; + +export type BeadDepsResponse = { + children: Array | null; +}; + +export type BeadEventPayload = { + bead: Bead; +}; + +export type BeadGraphResponse = { + beads: Array | null; + deps: Array | null; + root: Bead; +}; + +export type BeadUpdateBody = { + /** + * Assigned agent. + */ + assignee?: string; + /** + * Bead description. + */ + description?: string; + /** + * Bead labels. + */ + labels?: Array | null; + /** + * Metadata key-value pairs to set. + */ + metadata?: { + [key: string]: string; + }; + /** + * Parent bead ID. Use null or an empty string to clear. + */ + parent?: string | null; + /** + * Bead priority. + */ + priority?: number; + /** + * Labels to remove. + */ + remove_labels?: Array | null; + /** + * Bead status. + */ + status?: string; + /** + * Bead title. + */ + title?: string; + /** + * Bead type. + */ + type?: string; +}; + +/** + * Lifecycle state of a session binding. + */ +export type BindingStatus = 'active' | 'ended'; + +export type BoundEventPayload = { + conversation_id: string; + provider: string; + session_id: string; +}; + +export type CityCreateRequest = { + /** + * Optional bootstrap profile. + */ + bootstrap_profile?: 'k8s-cell' | 'kubernetes' | 'kubernetes-cell' | 'single-host-compat'; + /** + * Directory to create the city in. Absolute or relative to $HOME. + */ + dir: string; + /** + * Provider name for the city's default session template. + */ + provider: string; +}; + +export type CityCreateResponse = { + /** + * Resolved city name as persisted in city.toml. Use this to filter the event stream for completion. + */ + name: string; + /** + * True when scaffolding + registration succeeded. Does not imply the city is ready yet; watch /v0/events/stream for city.ready. + */ + ok: boolean; + /** + * Resolved absolute path of the created city directory. + */ + path: string; +}; + +export type CityGetResponse = { + agent_count: number; + name: string; + path: string; + provider?: string; + rig_count: number; + session_template?: string; + suspended: boolean; + uptime_sec: number; + version?: string; +}; + +export type CityInfo = { + error?: string; + name: string; + path: string; + phases_completed?: Array | null; + running: boolean; + status?: string; +}; + +export type CityLifecyclePayload = { + error?: string; + name: string; + path: string; + phases_completed?: Array | null; +}; + +export type CityPatchInputBody = { + /** + * Whether the city is suspended. + */ + suspended?: boolean; +}; + +export type CityUnregisterResponse = { + /** + * Resolved registry name. Filter the event stream by this to observe completion. + */ + name: string; + /** + * True when the registry entry was removed and the supervisor was signaled. Does not imply the city's controller has stopped yet; watch /v0/events/stream for city.unregistered. + */ + ok: boolean; + /** + * Resolved absolute city directory. The directory itself is not modified; unregister only affects the supervisor's registry. + */ + path: string; +}; + +export type ConfigAgentResponse = { + dir?: string; + is_pool?: boolean; + name: string; + provider?: string; + scope?: string; + suspended: boolean; +}; + +export type ConfigExplainPatches = { + agents: number; + providers: number; + rigs: number; +}; + +export type ConfigExplainResponse = { + agents: Array | null; + patches: ConfigExplainPatches; + providers: { + [key: string]: AnnotatedProviderResponse; + }; +}; + +export type ConfigPatchesResponse = { + agent_count: number; + provider_count: number; + rig_count: number; +}; + +export type ConfigResponse = { + agents: Array | null; + patches?: ConfigPatchesResponse; + providers?: { + [key: string]: ProviderSpecJson; + }; + rigs: Array | null; + workspace: WorkspaceResponse; +}; + +export type ConfigRigResponse = { + name: string; + path: string; + prefix?: string; + suspended: boolean; +}; + +export type ConfigValidateOutputBody = { + /** + * Validation errors. + */ + errors: Array | null; + /** + * Whether the configuration is valid. + */ + valid: boolean; + /** + * Validation warnings. + */ + warnings: Array | null; +}; + +export type ConversationGroupParticipant = { + GroupID: string; + Handle: string; + ID: string; + Metadata: { + [key: string]: string; + }; + Public: boolean; + SessionID: string; +}; + +export type ConversationGroupRecord = { + DefaultHandle: string; + FanoutPolicy: FanoutPolicy; + ID: string; + LastAddressedHandle: string; + Metadata: { + [key: string]: string; + }; + Mode: string; + RootConversation: ConversationRef; + SchemaVersion: number; +}; + +/** + * Shape of a conversation. + */ +export type ConversationKind = 'dm' | 'room' | 'thread'; + +export type ConversationRef = { + account_id: string; + conversation_id: string; + kind: ConversationKind; + parent_conversation_id?: string; + provider: string; + scope_id: string; +}; + +export type ConversationTranscriptRecord = { + Actor: ExternalActor; + Attachments: Array | null; + Conversation: ConversationRef; + CreatedAt: string; + ExplicitTarget: string; + ID: string; + Kind: TranscriptMessageKind; + Metadata: { + [key: string]: string; + }; + Provenance: TranscriptProvenance; + ProviderMessageID: string; + ReplyToMessageID: string; + SchemaVersion: number; + Sequence: number; + SourceSessionID: string; + Text: string; +}; + +export type ConvoyAddInputBody = { + /** + * Bead IDs to add. + */ + items?: Array | null; +}; + +export type ConvoyCheckResponse = { + /** + * Closed child bead count. + */ + closed: number; + /** + * True when all child beads are closed and total > 0. + */ + complete: boolean; + /** + * Convoy ID. + */ + convoy_id: string; + /** + * Total child bead count. + */ + total: number; +}; + +export type ConvoyCreateInputBody = { + /** + * Bead IDs to include. + */ + items?: Array | null; + /** + * Rig name. + */ + rig?: string; + /** + * Convoy title. + */ + title: string; +}; + +export type ConvoyGetResponse = { + /** + * Direct child beads (non-workflow case). + */ + children?: Array | null; + /** + * Simple convoy bead (non-workflow case). + */ + convoy?: Bead; + /** + * Child bead progress (non-workflow case). + */ + progress?: ConvoyProgress; +}; + +export type ConvoyProgress = { + /** + * Closed child bead count. + */ + closed: number; + /** + * Total child bead count. + */ + total: number; +}; + +export type ConvoyRemoveInputBody = { + /** + * Bead IDs to remove. + */ + items?: Array | null; +}; + +export type DeliveryContextRecord = { + BindingGeneration: number; + Conversation: ConversationRef; + ID: string; + LastMessageID: string; + LastPublishedAt: string; + Metadata: { + [key: string]: string; + }; + SchemaVersion: number; + SessionID: string; + SourceSessionID: string; +}; + +export type Dep = { + depends_on_id: string; + issue_id: string; + type: string; +}; + +export type ErrorDetail = { + /** + * Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id' + */ + location?: string; + /** + * Error message text + */ + message?: string; + /** + * The value at the given location + */ + value?: unknown; +}; + +export type ErrorModel = { + /** + * A human-readable explanation specific to this occurrence of the problem. + */ + detail?: string; + /** + * Optional list of individual error details + */ + errors?: Array | null; + /** + * A URI reference that identifies the specific occurrence of the problem. + */ + instance?: string; + /** + * HTTP status code + */ + status?: number; + /** + * A short, human-readable summary of the problem type. This value should not change between occurrences of the error. + */ + title?: string; + /** + * A URI reference to human-readable documentation for the error. + */ + type?: string; +}; + +export type EventEmitOutputBody = { + /** + * Operation result. + */ + status: string; +}; + +export type EventEmitRequest = { + /** + * Actor that produced the event. + */ + actor: string; + /** + * Event message. + */ + message?: string; + /** + * Event subject. + */ + subject?: string; + /** + * Event type. + */ + type: string; +}; + +export type EventPayload = AdapterEventPayload | BeadEventPayload | BoundEventPayload | CityLifecyclePayload | GroupCreatedEventPayload | InboundEventPayload | MailEventPayload | NoPayload | OutboundEventPayload | UnboundEventPayload | WorkerOperationEventPayload; + +export type EventStreamEnvelope = { + actor: string; + message?: string; + payload?: EventPayload; + seq: number; + subject?: string; + ts: string; + type: string; + workflow?: WorkflowEventProjection; +}; + +export type ExtMsgAdapterRegisterInputBody = { + /** + * Account ID. + */ + account_id: string; + /** + * Callback URL for outbound messages. + */ + callback_url?: string; + /** + * Adapter capabilities. + */ + capabilities?: AdapterCapabilities; + /** + * Adapter display name. + */ + name?: string; + /** + * Provider name. + */ + provider: string; +}; + +export type ExtMsgAdapterRegisterOutputBody = { + /** + * Account ID. + */ + account_id: string; + /** + * Adapter name. + */ + name: string; + /** + * Provider name. + */ + provider: string; + /** + * Operation result. + */ + status: string; +}; + +export type ExtMsgAdapterUnregisterInputBody = { + /** + * Account ID. + */ + account_id: string; + /** + * Provider name. + */ + provider: string; +}; + +export type ExtMsgBindInputBody = { + /** + * Conversation to bind. + */ + conversation?: ConversationRef; + /** + * Optional binding metadata. + */ + metadata?: { + [key: string]: string; + }; + /** + * Session ID to bind. + */ + session_id: string; +}; + +export type ExtMsgGroupEnsureInputBody = { + /** + * Default handle for the group. + */ + default_handle?: string; + /** + * Group metadata. + */ + metadata?: { + [key: string]: string; + }; + /** + * Group mode (launcher, etc.). + */ + mode?: string; + /** + * Root conversation reference. + */ + root_conversation?: ConversationRef; +}; + +export type ExtMsgInboundInputBody = { + /** + * Account ID for raw payloads (required when message is absent). + */ + account_id?: string; + /** + * Pre-normalized inbound message. + */ + message?: ExternalInboundMessage; + /** + * Raw payload bytes. + */ + payload?: string; + /** + * Provider name for raw payloads (required when message is absent). + */ + provider?: string; +}; + +export type ExtMsgOutboundInputBody = { + /** + * Target conversation. + */ + conversation?: ConversationRef; + /** + * Idempotency key. + */ + idempotency_key?: string; + /** + * Message ID to reply to. + */ + reply_to_message_id?: string; + /** + * Session ID. + */ + session_id: string; + /** + * Message text. + */ + text?: string; +}; + +export type ExtMsgParticipantRemoveInputBody = { + /** + * Group ID. + */ + group_id: string; + /** + * Participant handle. + */ + handle: string; +}; + +export type ExtMsgParticipantUpsertInputBody = { + /** + * Group ID. + */ + group_id: string; + /** + * Participant handle. + */ + handle: string; + /** + * Participant metadata. + */ + metadata?: { + [key: string]: string; + }; + /** + * Whether participant is public. + */ + public?: boolean; + /** + * Session ID. + */ + session_id: string; +}; + +export type ExtMsgTranscriptAckInputBody = { + /** + * Conversation to acknowledge. + */ + conversation?: ConversationRef; + /** + * Sequence number to acknowledge up to. + */ + sequence?: number; + /** + * Session ID. + */ + session_id: string; +}; + +export type ExtMsgUnbindBody = { + /** + * Bindings that were removed. + */ + unbound: Array | null; +}; + +export type ExtMsgUnbindInputBody = { + /** + * Conversation to unbind (nil = all). + */ + conversation?: ConversationRef; + /** + * Session ID to unbind. + */ + session_id: string; +}; + +export type ExternalActor = { + display_name: string; + id: string; + is_bot: boolean; +}; + +export type ExternalAttachment = { + mime_type: string; + provider_id: string; + url: string; +}; + +export type ExternalInboundMessage = { + actor: ExternalActor; + attachments?: Array | null; + conversation: ConversationRef; + dedup_key?: string; + explicit_target?: string; + provider_message_id: string; + received_at: string; + reply_to_message_id?: string; + text: string; +}; + +export type ExtmsgAdapterInfo = { + /** + * Adapter account ID. + */ + account_id: string; + /** + * Adapter display name. + */ + name: string; + /** + * Adapter provider key. + */ + provider: string; +}; + +export type FanoutPolicy = { + AllowUntargetedPublication: boolean; + Enabled: boolean; + MaxPeerTriggeredPublishes: number; + MaxTotalPeerDeliveries: number; +}; + +export type FormulaDetailResponse = { + deps: Array | null; + description: string; + name: string; + preview: FormulaPreviewResponse; + steps: Array | null; + var_defs: Array | null; + version: string; +}; + +export type FormulaFeedBody = { + items: Array | null; + partial: boolean; + partial_errors?: Array | null; +}; + +export type FormulaListBody = { + /** + * Formula summaries. + */ + items: Array | null; + /** + * Whether the list is partial. + */ + partial: boolean; +}; + +export type FormulaPreviewBody = { + /** + * Scope kind (city or rig). + */ + scope_kind?: string; + /** + * Scope reference. + */ + scope_ref?: string; + /** + * Target agent for preview compilation. + */ + target: string; + /** + * Variable name-to-value overrides applied to the compiled preview. + */ + vars?: { + [key: string]: string; + }; +}; + +export type FormulaPreviewEdgeResponse = { + from: string; + kind?: string; + to: string; +}; + +export type FormulaPreviewNodeResponse = { + id: string; + kind: string; + scope_ref?: string; + title: string; +}; + +export type FormulaPreviewResponse = { + edges: Array | null; + nodes: Array | null; +}; + +export type FormulaRecentRunResponse = { + started_at: string; + status: string; + target: string; + updated_at: string; + workflow_id: string; +}; + +export type FormulaRunsResponse = { + formula: string; + partial: boolean; + partial_errors?: Array | null; + recent_runs: Array | null; + run_count: number; +}; + +export type FormulaStepResponse = { + assignee?: string; + id: string; + kind: string; + labels?: Array | null; + metadata?: { + [key: string]: string; + }; + title: string; + type?: string; +}; + +export type FormulaSummaryResponse = { + description: string; + name: string; + recent_runs: Array | null; + run_count: number; + var_defs: Array | null; + version: string; +}; + +export type FormulaVarDefResponse = { + default?: unknown; + description?: string; + enum?: Array | null; + name: string; + pattern?: string; + required?: boolean; + type: string; +}; + +export type GitStatus = { + ahead: number; + behind: number; + branch: string; + changed_files: number; + clean: boolean; +}; + +export type GroupCreatedEventPayload = { + conversation_id: string; + mode: string; + provider: string; +}; + +export type GroupRouteDecision = { + Match: string; + TargetSessionID: string; + UpdateCursor: boolean; +}; + +export type HealthOutputBody = { + /** + * City name. + */ + city?: string; + /** + * Health status. + */ + status: string; + /** + * Server uptime in seconds. + */ + uptime_sec: number; + /** + * Server version. + */ + version?: string; +}; + +export type HeartbeatEvent = { + /** + * ISO 8601 timestamp when the heartbeat was sent. + */ + timestamp: string; +}; + +export type InboundEventPayload = { + actor: string; + conversation_id: string; + provider: string; + target_session: string; +}; + +export type InboundResult = { + Binding: SessionBindingRecord; + GroupRoute: GroupRouteDecision; + Message: ExternalInboundMessage; + TargetSessionID: string; + TranscriptEntry: ConversationTranscriptRecord; +}; + +export type ListBodyAgentPatch = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type ListBodyAgentResponse = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type ListBodyBead = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type ListBodyConversationTranscriptRecord = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type ListBodyExtmsgAdapterInfo = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type ListBodyProviderPatch = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type ListBodyProviderResponse = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type ListBodyRigPatch = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type ListBodyRigResponse = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type ListBodySessionBindingRecord = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type ListBodySessionResponse = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type ListBodyStatus = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type ListBodyWireEvent = { + /** + * The list of items. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more backends failed and the list is incomplete. + */ + partial?: boolean; + /** + * Human-readable errors from backends that failed during aggregation. + */ + partial_errors?: Array | null; + /** + * Total number of items matching the query. + */ + total: number; +}; + +export type LogicalNode = { + [key: string]: never; +}; + +export type MailCountOutputBody = { + /** + * True when one or more rig providers failed and the counts are not authoritative. + */ + partial?: boolean; + /** + * Per-provider errors when partial is true. + */ + partial_errors?: Array | null; + /** + * Total message count. + */ + total: number; + /** + * Unread message count. + */ + unread: number; +}; + +export type MailEventPayload = { + message?: Message; + rig: string; +}; + +export type MailListBody = { + /** + * The list of messages. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * True when one or more rig providers failed and the list is not authoritative. + */ + partial?: boolean; + /** + * Per-provider errors when partial is true. + */ + partial_errors?: Array | null; + /** + * Total number of messages matching the query. + */ + total: number; +}; + +export type MailReplyInputBody = { + /** + * Reply body. + */ + body?: string; + /** + * Sender name. + */ + from?: string; + /** + * Reply subject. + */ + subject?: string; +}; + +export type MailSendInputBody = { + /** + * Message body. + */ + body?: string; + /** + * Sender name. + */ + from?: string; + /** + * Rig name. + */ + rig?: string; + /** + * Message subject. + */ + subject: string; + /** + * Recipient name. + */ + to: string; +}; + +export type Message = { + body: string; + cc?: Array | null; + created_at: string; + from: string; + id: string; + priority?: number; + read: boolean; + reply_to?: string; + rig?: string; + subject: string; + thread_id?: string; + to: string; +}; + +export type MonitorFeedItemResponse = { + attached_bead_id?: string; + bead_id?: string; + detail_available?: boolean; + id: string; + logical_bead_id?: string; + root_bead_id?: string; + root_store_ref?: string; + run_detail_available?: boolean; + scope_kind: string; + scope_ref: string; + started_at: string; + status: string; + store_ref?: string; + target: string; + title: string; + type: string; + updated_at: string; + workflow_id?: string; +}; + +export type NoPayload = { + [key: string]: never; +}; + +export type OkResponseBody = { + /** + * Operation result. + */ + status: string; +}; + +export type OkWithIdResponseBody = { + /** + * Resource ID. + */ + id?: string; + /** + * Operation result. + */ + status: string; +}; + +export type OptionChoiceDto = { + label: string; + value: string; +}; + +export type OrderCheckListBody = { + /** + * Order trigger evaluations. + */ + checks: Array | null; +}; + +export type OrderCheckResponse = { + due: boolean; + last_run?: string; + last_run_outcome?: string; + name: string; + reason: string; + rig?: string; + scoped_name: string; +}; + +export type OrderHistoryDetailResponse = { + bead_id: string; + created_at: string; + labels: Array | null; + output: string; + store_ref: string; +}; + +export type OrderHistoryEntry = { + bead_id: string; + capture_output: boolean; + created_at: string; + duration_ms?: string; + error?: string; + exit_code?: string; + has_output: boolean; + labels: Array | null; + name: string; + rig?: string; + scoped_name: string; + signal?: string; + store_ref: string; + wisp_root_id?: string; +}; + +export type OrderHistoryListBody = { + /** + * Order history entries. + */ + entries: Array | null; +}; + +export type OrderListBody = { + /** + * Registered orders. + */ + orders: Array | null; +}; + +export type OrderResponse = { + capture_output: boolean; + check?: string; + description?: string; + enabled: boolean; + exec?: string; + formula?: string; + /** + * @deprecated + */ + gate?: string; + interval?: string; + name: string; + on?: string; + pool?: string; + rig?: string; + schedule?: string; + scoped_name: string; + timeout?: string; + timeout_ms: number; + trigger?: string; + type: string; +}; + +export type OrdersFeedBody = { + items: Array | null; + partial: boolean; + partial_errors?: Array | null; +}; + +export type OutboundEventPayload = { + conversation_id: string; + message_id: string; + provider: string; + session: string; +}; + +export type OutboundResult = { + DeliveryContext: DeliveryContextRecord; + Receipt: PublishReceipt; + TranscriptEntry: ConversationTranscriptRecord; +}; + +export type OutputTurn = { + role: string; + text: string; + timestamp?: string; +}; + +export type PackListBody = { + /** + * Registered packs. + */ + packs: Array | null; +}; + +export type PackResponse = { + name: string; + path?: string; + ref?: string; + source?: string; +}; + +export type PaginationInfo = { + has_older_messages: boolean; + returned_message_count: number; + total_compactions: number; + total_message_count: number; + truncated_before_message?: string; +}; + +export type PatchDeletedResponseBody = { + /** + * Agent patch qualified name. + */ + agent_patch?: string; + /** + * Provider patch name. + */ + provider_patch?: string; + /** + * Rig patch name. + */ + rig_patch?: string; + /** + * Operation result. + */ + status: string; +}; + +export type PatchOkResponseBody = { + /** + * Agent patch qualified name. + */ + agent_patch?: string; + /** + * Provider patch name. + */ + provider_patch?: string; + /** + * Rig patch name. + */ + rig_patch?: string; + /** + * Operation result. + */ + status: string; +}; + +export type PendingInteraction = { + kind: string; + metadata?: { + [key: string]: string; + }; + options?: Array | null; + prompt?: string; + request_id: string; +}; + +export type PoolOverride = { + Check: string | null; + DrainTimeout: string | null; + Max: number | null; + Min: number | null; + OnBoot: string | null; + OnDeath: string | null; +}; + +export type ProviderCreateInputBody = { + /** + * ACP transport command arguments override. + */ + acp_args?: Array | null; + /** + * ACP transport command binary override. + */ + acp_command?: string; + /** + * Command arguments. + */ + args?: Array | null; + /** + * Arguments appended after inherited/base args. + */ + args_append?: Array | null; + /** + * Optional provider base for inheritance. + */ + base?: string; + /** + * Provider command binary. Omit for base-only descendants. + */ + command?: string; + /** + * Human-readable display name. + */ + display_name?: string; + /** + * Environment variables. + */ + env?: { + [key: string]: string; + }; + /** + * Provider name. + */ + name: string; + /** + * Options schema merge mode across inheritance chain. + */ + options_schema_merge?: string; + /** + * Flag for prompt delivery. + */ + prompt_flag?: string; + /** + * Prompt delivery mode. + */ + prompt_mode?: string; + /** + * Milliseconds to wait before probing readiness. + */ + ready_delay_ms?: number; +}; + +export type ProviderCreatedOutputBody = { + /** + * Created provider name. + */ + provider: string; + /** + * Operation result. + */ + status: string; +}; + +export type ProviderOptionDto = { + choices: Array | null; + default: string; + key: string; + label: string; + type: string; +}; + +export type ProviderPatch = { + ACPArgs: Array | null; + ACPCommand: string | null; + Args: Array | null; + ArgsAppend: Array | null; + Base: string | null; + Command: string | null; + Env: { + [key: string]: string; + }; + EnvRemove: Array | null; + Name: string; + OptionsSchemaMerge: string | null; + PromptFlag: string | null; + PromptMode: string | null; + ReadyDelayMs: number | null; + Replace: boolean; +}; + +export type ProviderPatchSetInputBody = { + /** + * Override ACP transport command arguments. + */ + acp_args?: Array | null; + /** + * Override ACP transport command binary. + */ + acp_command?: string; + /** + * Override command arguments. + */ + args?: Array | null; + /** + * Override command binary. + */ + command?: string; + /** + * Override environment variables. + */ + env?: { + [key: string]: string; + }; + /** + * Provider name. + */ + name?: string; + /** + * Override prompt flag. + */ + prompt_flag?: string; + /** + * Override prompt delivery mode. + */ + prompt_mode?: string; + /** + * Override ready delay in milliseconds. + */ + ready_delay_ms?: number; +}; + +export type ProviderPublicListBody = { + /** + * The list of browser-safe provider summaries. + */ + items: Array | null; + /** + * Cursor for the next page of results. + */ + next_cursor?: string; + /** + * Total number of providers in the list. + */ + total: number; +}; + +export type ProviderPublicResponse = { + builtin: boolean; + city_level: boolean; + display_name?: string; + effective_defaults?: { + [key: string]: string; + }; + name: string; + options_schema?: Array | null; +}; + +export type ProviderReadiness = { + detail?: string; + display_name: string; + status: string; +}; + +export type ProviderReadinessResponse = { + providers: { + [key: string]: ProviderReadiness; + }; +}; + +export type ProviderResponse = { + acp_args?: Array; + acp_command?: string; + args?: Array | null; + builtin: boolean; + city_level: boolean; + command?: string; + display_name?: string; + env?: { + [key: string]: string; + }; + name: string; + prompt_flag?: string; + prompt_mode?: string; + ready_delay_ms?: number; +}; + +export type ProviderSpecJson = { + acp_args?: Array; + acp_command?: string; + args?: Array | null; + command?: string; + display_name?: string; + env?: { + [key: string]: string; + }; + prompt_flag?: string; + prompt_mode?: string; + ready_delay_ms?: number; +}; + +export type ProviderUpdateInputBody = { + /** + * ACP transport command arguments override. + */ + acp_args?: Array | null; + /** + * ACP transport command binary override. + */ + acp_command?: string; + /** + * Command arguments. + */ + args?: Array | null; + /** + * Arguments appended after inherited/base args. + */ + args_append?: Array | null; + /** + * Provider base for inheritance. + */ + base?: string; + /** + * Provider command binary. + */ + command?: string; + /** + * Human-readable display name. + */ + display_name?: string; + /** + * Environment variables. + */ + env?: { + [key: string]: string; + }; + /** + * Options schema merge mode across inheritance chain. + */ + options_schema_merge?: string; + /** + * Flag for prompt delivery. + */ + prompt_flag?: string; + /** + * Prompt delivery mode. + */ + prompt_mode?: string; + /** + * Milliseconds to wait before probing readiness. + */ + ready_delay_ms?: number; +}; + +export type PublishReceipt = { + Conversation: ConversationRef; + Delivered: boolean; + FailureKind: string; + MessageID: string; + Metadata: { + [key: string]: string; + }; + RetryAfter: number; +}; + +export type ReadinessItem = { + detail?: string; + display_name: string; + kind: string; + name: string; + status: string; +}; + +export type ReadinessResponse = { + items: { + [key: string]: ReadinessItem; + }; +}; + +export type RigActionBody = { + /** + * Action that was performed. + */ + action: string; + /** + * Agents that failed to stop (restart only). + */ + failed?: Array | null; + /** + * Agents that were killed (restart only). + */ + killed?: Array | null; + /** + * Rig name. + */ + rig: string; + /** + * Operation result (ok, partial, failed). + */ + status: string; +}; + +export type RigCreateInputBody = { + /** + * Rig name. + */ + name: string; + /** + * Filesystem path. + */ + path: string; + /** + * Session name prefix. + */ + prefix?: string; +}; + +export type RigCreatedOutputBody = { + /** + * Created rig name. + */ + rig: string; + /** + * Operation result. + */ + status: string; +}; + +export type RigPatch = { + Name: string; + Path: string | null; + Prefix: string | null; + Suspended: boolean | null; +}; + +export type RigPatchSetInputBody = { + /** + * Rig name. + */ + name?: string; + /** + * Override filesystem path. + */ + path?: string; + /** + * Override bead ID prefix. + */ + prefix?: string; + /** + * Override suspended state. + */ + suspended?: boolean; +}; + +export type RigResponse = { + agent_count: number; + git?: GitStatus; + last_activity?: string; + name: string; + path: string; + prefix?: string; + running_count: number; + suspended: boolean; +}; + +export type RigUpdateInputBody = { + /** + * Filesystem path. + */ + path?: string; + /** + * Session name prefix. + */ + prefix?: string; + /** + * Whether rig is suspended. + */ + suspended?: boolean; +}; + +export type ScopeGroup = { + [key: string]: never; +}; + +export type ServiceRestartOutputBody = { + /** + * Action performed. + */ + action: string; + /** + * Service name. + */ + service: string; + /** + * Operation result. + */ + status: string; +}; + +export type SessionActivityEvent = { + /** + * Session activity state: 'idle' or 'in-turn'. + */ + activity: string; +}; + +export type SessionAgentGetResponse = { + messages: Array | null; + status?: string; +}; + +export type SessionAgentListResponse = { + agents: Array | null; +}; + +export type SessionBindingRecord = { + BindingGeneration: number; + BoundAt: string; + Conversation: ConversationRef; + ExpiresAt: string | null; + ID: string; + Metadata: { + [key: string]: string; + }; + SchemaVersion: number; + SessionID: string; + Status: BindingStatus; +}; + +export type SessionCreateBody = { + /** + * Optional session alias. + */ + alias?: string; + /** + * Create session asynchronously (agent only). + */ + async?: boolean; + /** + * Session target kind: agent or provider. + */ + kind?: string; + /** + * Initial message to send to the session. + */ + message?: string; + /** + * Agent or provider name. + */ + name?: string; + /** + * Provider/agent option overrides. + */ + options?: { + [key: string]: string; + }; + /** + * Opaque project context identifier. + */ + project_id?: string; + /** + * Deprecated: use alias. + */ + session_name?: string; + /** + * Session title. + */ + title?: string; +}; + +export type SessionInfo = { + attached: boolean; + last_activity?: string; + name: string; +}; + +export type SessionMessageInputBody = { + /** + * Message text to send. + */ + message: string; +}; + +export type SessionMessageOutputBody = { + /** + * Session ID. + */ + id: string; + /** + * Operation result. + */ + status: string; +}; + +export type SessionPatchBody = { + /** + * Session alias. Empty string clears the alias. + */ + alias?: string; + /** + * Session title. If provided, must be non-empty. + */ + title?: string; +}; + +export type SessionPendingResponse = { + pending?: PendingInteraction; + supported: boolean; +}; + +/** + * Session raw transcript frame + * + * Provider-native transcript frame. Gas City forwards the exact JSON the provider wrote to its session log, so the shape is provider-specific and can be any JSON value. The producing provider is identified by the Provider field on the enclosing envelope; consumers dispatch per-provider frame parsing keyed by that identifier. + */ +export type SessionRawMessageFrame = unknown; + +export type SessionRenameInputBody = { + /** + * New session title. + */ + title: string; +}; + +export type SessionRespondInputBody = { + /** + * Response action (e.g. allow, deny). + */ + action: string; + /** + * Optional response metadata. + */ + metadata?: { + [key: string]: string; + }; + /** + * Pending interaction request ID (optional). + */ + request_id?: string; + /** + * Optional response text. + */ + text?: string; +}; + +export type SessionRespondOutputBody = { + /** + * Session ID. + */ + id: string; + /** + * Operation result. + */ + status: string; +}; + +export type SessionResponse = { + active_bead?: string; + activity?: string; + alias?: string; + attached: boolean; + configured_named_session?: boolean; + context_pct?: number; + context_window?: number; + created_at: string; + display_name?: string; + id: string; + kind?: string; + last_active?: string; + last_output?: string; + metadata?: { + [key: string]: string; + }; + model?: string; + options?: { + [key: string]: string; + }; + pool?: string; + provider: string; + reason?: string; + rig?: string; + running: boolean; + session_name: string; + state: string; + submission_capabilities?: SubmissionCapabilities; + template: string; + title: string; +}; + +/** + * Session stream lifecycle event + * + * Non-message events emitted on the session SSE stream: activity transitions, pending interactions, and keepalive heartbeats. The concrete variant is identified by the SSE event name. + */ +export type SessionStreamCommonEvent = SessionActivityEvent | PendingInteraction | HeartbeatEvent; + +export type SessionStreamMessageEvent = { + format: string; + id: string; + pagination?: PaginationInfo; + /** + * Producing provider identifier (claude, codex, gemini, open-code, etc.). + */ + provider: string; + template: string; + turns: Array | null; +}; + +export type SessionStreamRawMessageEvent = { + format: string; + id: string; + /** + * Provider-native transcript frames, emitted verbatim as the provider wrote them. + */ + messages: Array | null; + pagination?: PaginationInfo; + /** + * Producing provider identifier (claude, codex, gemini, open-code, etc.). Consumers use this to dispatch per-provider frame parsing. + */ + provider: string; + template: string; +}; + +export type SessionSubmitInputBody = { + /** + * Submit intent; empty defaults to "default". + */ + intent?: SubmitIntent; + /** + * Message text to submit. + */ + message: string; +}; + +export type SessionSubmitOutputBody = { + /** + * Session ID. + */ + id: string; + /** + * Resolved submit intent. + */ + intent: string; + /** + * Whether the message was queued. + */ + queued: boolean; + /** + * Operation result. + */ + status: string; +}; + +export type SessionTranscriptGetResponse = { + /** + * conversation, text, or raw. + */ + format: string; + id: string; + /** + * Populated for raw format; provider-native frames emitted verbatim as the provider wrote them. + */ + messages?: Array | null; + pagination?: PaginationInfo; + /** + * Producing provider identifier (claude, codex, gemini, open-code, etc.). Consumers use this to dispatch per-provider frame parsing. + */ + provider: string; + template: string; + /** + * Populated for conversation/text formats. + */ + turns?: Array | null; +}; + +export type SlingInputBody = { + /** + * Bead ID to attach a formula to. + */ + attached_bead_id?: string; + /** + * Bead ID to sling. + */ + bead?: string; + /** + * Bypass cross-rig guards; for direct bead routes, also bypass missing-bead validation. Formula-backed graph routes may replace existing live workflow roots but still require the source bead to exist. + */ + force?: boolean; + /** + * Formula name for workflow launch. + */ + formula?: string; + /** + * Rig name. + */ + rig?: string; + /** + * Scope kind (city or rig). + */ + scope_kind?: string; + /** + * Scope reference. + */ + scope_ref?: string; + /** + * Target agent or pool. + */ + target: string; + /** + * Workflow title. + */ + title?: string; + /** + * Formula variables. + */ + vars?: { + [key: string]: string; + }; +}; + +export type SlingResponse = { + attached_bead_id?: string; + bead?: string; + formula?: string; + mode?: string; + root_bead_id?: string; + status: string; + target: string; + warnings?: Array | null; + workflow_id?: string; +}; + +export type Status = { + allow_websockets?: boolean; + hostname?: string; + kind?: string; + local_state: string; + mount_path: string; + publication_state: string; + publish_mode: string; + reason?: string; + service_name: string; + state?: string; + state_root: string; + updated_at: string; + url?: string; + visibility?: string; + workflow_contract?: string; +}; + +export type StatusAgentCounts = { + /** + * Number of quarantined agents. + */ + quarantined: number; + /** + * Number of running agents. + */ + running: number; + /** + * Number of suspended agents. + */ + suspended: number; + /** + * Total number of agents. + */ + total: number; +}; + +export type StatusBody = { + /** + * Total agent count (deprecated, use agents.total). + */ + agent_count: number; + /** + * Agent state counts. + */ + agents: StatusAgentCounts; + /** + * Mail counts. + */ + mail: StatusMailCounts; + /** + * City name. + */ + name: string; + /** + * City directory path. + */ + path: string; + /** + * Total rig count (deprecated, use rigs.total). + */ + rig_count: number; + /** + * Rig state counts. + */ + rigs: StatusRigCounts; + /** + * Number of running agent processes. + */ + running: number; + /** + * Whether the city is suspended. + */ + suspended: boolean; + /** + * Server uptime in seconds. + */ + uptime_sec: number; + /** + * Server version. + */ + version?: string; + /** + * Work item counts. + */ + work: StatusWorkCounts; +}; + +export type StatusMailCounts = { + /** + * Total number of messages. + */ + total: number; + /** + * Number of unread messages. + */ + unread: number; +}; + +export type StatusRigCounts = { + /** + * Number of suspended rigs. + */ + suspended: number; + /** + * Total number of rigs. + */ + total: number; +}; + +export type StatusWorkCounts = { + /** + * Number of in-progress work items. + */ + in_progress: number; + /** + * Number of open work items. + */ + open: number; + /** + * Number of ready work items. + */ + ready: number; +}; + +export type SubmissionCapabilities = { + supports_follow_up: boolean; + supports_interrupt_now: boolean; +}; + +/** + * Semantic delivery choice for a user message on a session submit request. + */ +export type SubmitIntent = 'default' | 'follow_up' | 'interrupt_now'; + +export type SupervisorCitiesOutputBody = { + /** + * Managed cities with status info. + */ + items: Array | null; + /** + * Total count. + */ + total: number; +}; + +export type SupervisorEventListOutputBody = { + items: Array | null; + total: number; +}; + +export type SupervisorHealthOutputBody = { + /** + * Cities currently running. + */ + cities_running: number; + /** + * Total managed cities. + */ + cities_total: number; + /** + * First-city startup info for single-city deployments. + */ + startup?: SupervisorStartup; + /** + * Health status ("ok"). + */ + status: string; + /** + * Supervisor uptime in seconds. + */ + uptime_sec: number; + /** + * Supervisor version. + */ + version: string; +}; + +export type SupervisorStartup = { + /** + * Current phase (when not ready). + */ + phase?: string; + /** + * Phases completed so far. + */ + phases_completed?: Array | null; + /** + * True when the city is running. + */ + ready: boolean; +}; + +export type TaggedEventStreamEnvelope = { + actor: string; + city: string; + message?: string; + payload?: EventPayload; + seq: number; + subject?: string; + ts: string; + type: string; + workflow?: WorkflowEventProjection; +}; + +/** + * Direction of a transcript entry. + */ +export type TranscriptMessageKind = 'inbound' | 'outbound'; + +/** + * Provenance of a transcript entry (freshly observed vs. replayed from persisted history). + */ +export type TranscriptProvenance = 'live' | 'hydrated'; + +/** + * Typed city event stream envelope + * + * Discriminated union of city event stream envelopes. Each variant constrains the envelope type and payload schema together. + */ +export type TypedEventStreamEnvelope = ({ + type: 'bead.closed'; +} & TypedEventStreamEnvelopeBeadClosed) | ({ + type: 'bead.created'; +} & TypedEventStreamEnvelopeBeadCreated) | ({ + type: 'bead.updated'; +} & TypedEventStreamEnvelopeBeadUpdated) | ({ + type: 'city.created'; +} & TypedEventStreamEnvelopeCityCreated) | ({ + type: 'city.init_failed'; +} & TypedEventStreamEnvelopeCityInitFailed) | ({ + type: 'city.ready'; +} & TypedEventStreamEnvelopeCityReady) | ({ + type: 'city.resumed'; +} & TypedEventStreamEnvelopeCityResumed) | ({ + type: 'city.suspended'; +} & TypedEventStreamEnvelopeCitySuspended) | ({ + type: 'city.unregister_failed'; +} & TypedEventStreamEnvelopeCityUnregisterFailed) | ({ + type: 'city.unregister_requested'; +} & TypedEventStreamEnvelopeCityUnregisterRequested) | ({ + type: 'city.unregistered'; +} & TypedEventStreamEnvelopeCityUnregistered) | ({ + type: 'controller.started'; +} & TypedEventStreamEnvelopeControllerStarted) | ({ + type: 'controller.stopped'; +} & TypedEventStreamEnvelopeControllerStopped) | ({ + type: 'convoy.closed'; +} & TypedEventStreamEnvelopeConvoyClosed) | ({ + type: 'convoy.created'; +} & TypedEventStreamEnvelopeConvoyCreated) | ({ + type: 'extmsg.adapter_added'; +} & TypedEventStreamEnvelopeExtmsgAdapterAdded) | ({ + type: 'extmsg.adapter_removed'; +} & TypedEventStreamEnvelopeExtmsgAdapterRemoved) | ({ + type: 'extmsg.bound'; +} & TypedEventStreamEnvelopeExtmsgBound) | ({ + type: 'extmsg.group_created'; +} & TypedEventStreamEnvelopeExtmsgGroupCreated) | ({ + type: 'extmsg.inbound'; +} & TypedEventStreamEnvelopeExtmsgInbound) | ({ + type: 'extmsg.outbound'; +} & TypedEventStreamEnvelopeExtmsgOutbound) | ({ + type: 'extmsg.unbound'; +} & TypedEventStreamEnvelopeExtmsgUnbound) | ({ + type: 'mail.archived'; +} & TypedEventStreamEnvelopeMailArchived) | ({ + type: 'mail.deleted'; +} & TypedEventStreamEnvelopeMailDeleted) | ({ + type: 'mail.marked_read'; +} & TypedEventStreamEnvelopeMailMarkedRead) | ({ + type: 'mail.marked_unread'; +} & TypedEventStreamEnvelopeMailMarkedUnread) | ({ + type: 'mail.read'; +} & TypedEventStreamEnvelopeMailRead) | ({ + type: 'mail.replied'; +} & TypedEventStreamEnvelopeMailReplied) | ({ + type: 'mail.sent'; +} & TypedEventStreamEnvelopeMailSent) | ({ + type: 'order.completed'; +} & TypedEventStreamEnvelopeOrderCompleted) | ({ + type: 'order.failed'; +} & TypedEventStreamEnvelopeOrderFailed) | ({ + type: 'order.fired'; +} & TypedEventStreamEnvelopeOrderFired) | ({ + type: 'provider.swapped'; +} & TypedEventStreamEnvelopeProviderSwapped) | ({ + type: 'session.crashed'; +} & TypedEventStreamEnvelopeSessionCrashed) | ({ + type: 'session.draining'; +} & TypedEventStreamEnvelopeSessionDraining) | ({ + type: 'session.idle_killed'; +} & TypedEventStreamEnvelopeSessionIdleKilled) | ({ + type: 'session.quarantined'; +} & TypedEventStreamEnvelopeSessionQuarantined) | ({ + type: 'session.stopped'; +} & TypedEventStreamEnvelopeSessionStopped) | ({ + type: 'session.suspended'; +} & TypedEventStreamEnvelopeSessionSuspended) | ({ + type: 'session.undrained'; +} & TypedEventStreamEnvelopeSessionUndrained) | ({ + type: 'session.updated'; +} & TypedEventStreamEnvelopeSessionUpdated) | ({ + type: 'session.woke'; +} & TypedEventStreamEnvelopeSessionWoke) | ({ + type: 'worker.operation'; +} & TypedEventStreamEnvelopeWorkerOperation); + +/** + * TypedEventStreamEnvelope bead.closed + */ +export type TypedEventStreamEnvelopeBeadClosed = { + actor: string; + message?: string; + payload: BeadEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'bead.closed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope bead.created + */ +export type TypedEventStreamEnvelopeBeadCreated = { + actor: string; + message?: string; + payload: BeadEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'bead.created'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope bead.updated + */ +export type TypedEventStreamEnvelopeBeadUpdated = { + actor: string; + message?: string; + payload: BeadEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'bead.updated'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope city.created + */ +export type TypedEventStreamEnvelopeCityCreated = { + actor: string; + message?: string; + payload: CityLifecyclePayload; + seq: number; + subject?: string; + ts: string; + type: 'city.created'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope city.init_failed + */ +export type TypedEventStreamEnvelopeCityInitFailed = { + actor: string; + message?: string; + payload: CityLifecyclePayload; + seq: number; + subject?: string; + ts: string; + type: 'city.init_failed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope city.ready + */ +export type TypedEventStreamEnvelopeCityReady = { + actor: string; + message?: string; + payload: CityLifecyclePayload; + seq: number; + subject?: string; + ts: string; + type: 'city.ready'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope city.resumed + */ +export type TypedEventStreamEnvelopeCityResumed = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'city.resumed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope city.suspended + */ +export type TypedEventStreamEnvelopeCitySuspended = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'city.suspended'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope city.unregister_failed + */ +export type TypedEventStreamEnvelopeCityUnregisterFailed = { + actor: string; + message?: string; + payload: CityLifecyclePayload; + seq: number; + subject?: string; + ts: string; + type: 'city.unregister_failed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope city.unregister_requested + */ +export type TypedEventStreamEnvelopeCityUnregisterRequested = { + actor: string; + message?: string; + payload: CityLifecyclePayload; + seq: number; + subject?: string; + ts: string; + type: 'city.unregister_requested'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope city.unregistered + */ +export type TypedEventStreamEnvelopeCityUnregistered = { + actor: string; + message?: string; + payload: CityLifecyclePayload; + seq: number; + subject?: string; + ts: string; + type: 'city.unregistered'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope controller.started + */ +export type TypedEventStreamEnvelopeControllerStarted = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'controller.started'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope controller.stopped + */ +export type TypedEventStreamEnvelopeControllerStopped = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'controller.stopped'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope convoy.closed + */ +export type TypedEventStreamEnvelopeConvoyClosed = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'convoy.closed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope convoy.created + */ +export type TypedEventStreamEnvelopeConvoyCreated = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'convoy.created'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope extmsg.adapter_added + */ +export type TypedEventStreamEnvelopeExtmsgAdapterAdded = { + actor: string; + message?: string; + payload: AdapterEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.adapter_added'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope extmsg.adapter_removed + */ +export type TypedEventStreamEnvelopeExtmsgAdapterRemoved = { + actor: string; + message?: string; + payload: AdapterEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.adapter_removed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope extmsg.bound + */ +export type TypedEventStreamEnvelopeExtmsgBound = { + actor: string; + message?: string; + payload: BoundEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.bound'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope extmsg.group_created + */ +export type TypedEventStreamEnvelopeExtmsgGroupCreated = { + actor: string; + message?: string; + payload: GroupCreatedEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.group_created'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope extmsg.inbound + */ +export type TypedEventStreamEnvelopeExtmsgInbound = { + actor: string; + message?: string; + payload: InboundEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.inbound'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope extmsg.outbound + */ +export type TypedEventStreamEnvelopeExtmsgOutbound = { + actor: string; + message?: string; + payload: OutboundEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.outbound'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope extmsg.unbound + */ +export type TypedEventStreamEnvelopeExtmsgUnbound = { + actor: string; + message?: string; + payload: UnboundEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.unbound'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope mail.archived + */ +export type TypedEventStreamEnvelopeMailArchived = { + actor: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.archived'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope mail.deleted + */ +export type TypedEventStreamEnvelopeMailDeleted = { + actor: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.deleted'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope mail.marked_read + */ +export type TypedEventStreamEnvelopeMailMarkedRead = { + actor: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.marked_read'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope mail.marked_unread + */ +export type TypedEventStreamEnvelopeMailMarkedUnread = { + actor: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.marked_unread'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope mail.read + */ +export type TypedEventStreamEnvelopeMailRead = { + actor: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.read'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope mail.replied + */ +export type TypedEventStreamEnvelopeMailReplied = { + actor: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.replied'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope mail.sent + */ +export type TypedEventStreamEnvelopeMailSent = { + actor: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.sent'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope order.completed + */ +export type TypedEventStreamEnvelopeOrderCompleted = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'order.completed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope order.failed + */ +export type TypedEventStreamEnvelopeOrderFailed = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'order.failed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope order.fired + */ +export type TypedEventStreamEnvelopeOrderFired = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'order.fired'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope provider.swapped + */ +export type TypedEventStreamEnvelopeProviderSwapped = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'provider.swapped'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope session.crashed + */ +export type TypedEventStreamEnvelopeSessionCrashed = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.crashed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope session.draining + */ +export type TypedEventStreamEnvelopeSessionDraining = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.draining'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope session.idle_killed + */ +export type TypedEventStreamEnvelopeSessionIdleKilled = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.idle_killed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope session.quarantined + */ +export type TypedEventStreamEnvelopeSessionQuarantined = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.quarantined'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope session.stopped + */ +export type TypedEventStreamEnvelopeSessionStopped = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.stopped'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope session.suspended + */ +export type TypedEventStreamEnvelopeSessionSuspended = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.suspended'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope session.undrained + */ +export type TypedEventStreamEnvelopeSessionUndrained = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.undrained'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope session.updated + */ +export type TypedEventStreamEnvelopeSessionUpdated = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.updated'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope session.woke + */ +export type TypedEventStreamEnvelopeSessionWoke = { + actor: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.woke'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedEventStreamEnvelope worker.operation + */ +export type TypedEventStreamEnvelopeWorkerOperation = { + actor: string; + message?: string; + payload: WorkerOperationEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'worker.operation'; + workflow?: WorkflowEventProjection; +}; + +/** + * Typed supervisor event stream envelope + * + * Discriminated union of supervisor event stream envelopes. Each variant constrains the envelope type and payload schema together and includes the source city. + */ +export type TypedTaggedEventStreamEnvelope = ({ + type: 'bead.closed'; +} & TypedTaggedEventStreamEnvelopeBeadClosed) | ({ + type: 'bead.created'; +} & TypedTaggedEventStreamEnvelopeBeadCreated) | ({ + type: 'bead.updated'; +} & TypedTaggedEventStreamEnvelopeBeadUpdated) | ({ + type: 'city.created'; +} & TypedTaggedEventStreamEnvelopeCityCreated) | ({ + type: 'city.init_failed'; +} & TypedTaggedEventStreamEnvelopeCityInitFailed) | ({ + type: 'city.ready'; +} & TypedTaggedEventStreamEnvelopeCityReady) | ({ + type: 'city.resumed'; +} & TypedTaggedEventStreamEnvelopeCityResumed) | ({ + type: 'city.suspended'; +} & TypedTaggedEventStreamEnvelopeCitySuspended) | ({ + type: 'city.unregister_failed'; +} & TypedTaggedEventStreamEnvelopeCityUnregisterFailed) | ({ + type: 'city.unregister_requested'; +} & TypedTaggedEventStreamEnvelopeCityUnregisterRequested) | ({ + type: 'city.unregistered'; +} & TypedTaggedEventStreamEnvelopeCityUnregistered) | ({ + type: 'controller.started'; +} & TypedTaggedEventStreamEnvelopeControllerStarted) | ({ + type: 'controller.stopped'; +} & TypedTaggedEventStreamEnvelopeControllerStopped) | ({ + type: 'convoy.closed'; +} & TypedTaggedEventStreamEnvelopeConvoyClosed) | ({ + type: 'convoy.created'; +} & TypedTaggedEventStreamEnvelopeConvoyCreated) | ({ + type: 'extmsg.adapter_added'; +} & TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded) | ({ + type: 'extmsg.adapter_removed'; +} & TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved) | ({ + type: 'extmsg.bound'; +} & TypedTaggedEventStreamEnvelopeExtmsgBound) | ({ + type: 'extmsg.group_created'; +} & TypedTaggedEventStreamEnvelopeExtmsgGroupCreated) | ({ + type: 'extmsg.inbound'; +} & TypedTaggedEventStreamEnvelopeExtmsgInbound) | ({ + type: 'extmsg.outbound'; +} & TypedTaggedEventStreamEnvelopeExtmsgOutbound) | ({ + type: 'extmsg.unbound'; +} & TypedTaggedEventStreamEnvelopeExtmsgUnbound) | ({ + type: 'mail.archived'; +} & TypedTaggedEventStreamEnvelopeMailArchived) | ({ + type: 'mail.deleted'; +} & TypedTaggedEventStreamEnvelopeMailDeleted) | ({ + type: 'mail.marked_read'; +} & TypedTaggedEventStreamEnvelopeMailMarkedRead) | ({ + type: 'mail.marked_unread'; +} & TypedTaggedEventStreamEnvelopeMailMarkedUnread) | ({ + type: 'mail.read'; +} & TypedTaggedEventStreamEnvelopeMailRead) | ({ + type: 'mail.replied'; +} & TypedTaggedEventStreamEnvelopeMailReplied) | ({ + type: 'mail.sent'; +} & TypedTaggedEventStreamEnvelopeMailSent) | ({ + type: 'order.completed'; +} & TypedTaggedEventStreamEnvelopeOrderCompleted) | ({ + type: 'order.failed'; +} & TypedTaggedEventStreamEnvelopeOrderFailed) | ({ + type: 'order.fired'; +} & TypedTaggedEventStreamEnvelopeOrderFired) | ({ + type: 'provider.swapped'; +} & TypedTaggedEventStreamEnvelopeProviderSwapped) | ({ + type: 'session.crashed'; +} & TypedTaggedEventStreamEnvelopeSessionCrashed) | ({ + type: 'session.draining'; +} & TypedTaggedEventStreamEnvelopeSessionDraining) | ({ + type: 'session.idle_killed'; +} & TypedTaggedEventStreamEnvelopeSessionIdleKilled) | ({ + type: 'session.quarantined'; +} & TypedTaggedEventStreamEnvelopeSessionQuarantined) | ({ + type: 'session.stopped'; +} & TypedTaggedEventStreamEnvelopeSessionStopped) | ({ + type: 'session.suspended'; +} & TypedTaggedEventStreamEnvelopeSessionSuspended) | ({ + type: 'session.undrained'; +} & TypedTaggedEventStreamEnvelopeSessionUndrained) | ({ + type: 'session.updated'; +} & TypedTaggedEventStreamEnvelopeSessionUpdated) | ({ + type: 'session.woke'; +} & TypedTaggedEventStreamEnvelopeSessionWoke) | ({ + type: 'worker.operation'; +} & TypedTaggedEventStreamEnvelopeWorkerOperation); + +/** + * TypedTaggedEventStreamEnvelope bead.closed + */ +export type TypedTaggedEventStreamEnvelopeBeadClosed = { + actor: string; + city: string; + message?: string; + payload: BeadEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'bead.closed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope bead.created + */ +export type TypedTaggedEventStreamEnvelopeBeadCreated = { + actor: string; + city: string; + message?: string; + payload: BeadEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'bead.created'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope bead.updated + */ +export type TypedTaggedEventStreamEnvelopeBeadUpdated = { + actor: string; + city: string; + message?: string; + payload: BeadEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'bead.updated'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope city.created + */ +export type TypedTaggedEventStreamEnvelopeCityCreated = { + actor: string; + city: string; + message?: string; + payload: CityLifecyclePayload; + seq: number; + subject?: string; + ts: string; + type: 'city.created'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope city.init_failed + */ +export type TypedTaggedEventStreamEnvelopeCityInitFailed = { + actor: string; + city: string; + message?: string; + payload: CityLifecyclePayload; + seq: number; + subject?: string; + ts: string; + type: 'city.init_failed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope city.ready + */ +export type TypedTaggedEventStreamEnvelopeCityReady = { + actor: string; + city: string; + message?: string; + payload: CityLifecyclePayload; + seq: number; + subject?: string; + ts: string; + type: 'city.ready'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope city.resumed + */ +export type TypedTaggedEventStreamEnvelopeCityResumed = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'city.resumed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope city.suspended + */ +export type TypedTaggedEventStreamEnvelopeCitySuspended = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'city.suspended'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope city.unregister_failed + */ +export type TypedTaggedEventStreamEnvelopeCityUnregisterFailed = { + actor: string; + city: string; + message?: string; + payload: CityLifecyclePayload; + seq: number; + subject?: string; + ts: string; + type: 'city.unregister_failed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope city.unregister_requested + */ +export type TypedTaggedEventStreamEnvelopeCityUnregisterRequested = { + actor: string; + city: string; + message?: string; + payload: CityLifecyclePayload; + seq: number; + subject?: string; + ts: string; + type: 'city.unregister_requested'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope city.unregistered + */ +export type TypedTaggedEventStreamEnvelopeCityUnregistered = { + actor: string; + city: string; + message?: string; + payload: CityLifecyclePayload; + seq: number; + subject?: string; + ts: string; + type: 'city.unregistered'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope controller.started + */ +export type TypedTaggedEventStreamEnvelopeControllerStarted = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'controller.started'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope controller.stopped + */ +export type TypedTaggedEventStreamEnvelopeControllerStopped = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'controller.stopped'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope convoy.closed + */ +export type TypedTaggedEventStreamEnvelopeConvoyClosed = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'convoy.closed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope convoy.created + */ +export type TypedTaggedEventStreamEnvelopeConvoyCreated = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'convoy.created'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope extmsg.adapter_added + */ +export type TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded = { + actor: string; + city: string; + message?: string; + payload: AdapterEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.adapter_added'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope extmsg.adapter_removed + */ +export type TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved = { + actor: string; + city: string; + message?: string; + payload: AdapterEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.adapter_removed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope extmsg.bound + */ +export type TypedTaggedEventStreamEnvelopeExtmsgBound = { + actor: string; + city: string; + message?: string; + payload: BoundEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.bound'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope extmsg.group_created + */ +export type TypedTaggedEventStreamEnvelopeExtmsgGroupCreated = { + actor: string; + city: string; + message?: string; + payload: GroupCreatedEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.group_created'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope extmsg.inbound + */ +export type TypedTaggedEventStreamEnvelopeExtmsgInbound = { + actor: string; + city: string; + message?: string; + payload: InboundEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.inbound'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope extmsg.outbound + */ +export type TypedTaggedEventStreamEnvelopeExtmsgOutbound = { + actor: string; + city: string; + message?: string; + payload: OutboundEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.outbound'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope extmsg.unbound + */ +export type TypedTaggedEventStreamEnvelopeExtmsgUnbound = { + actor: string; + city: string; + message?: string; + payload: UnboundEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'extmsg.unbound'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope mail.archived + */ +export type TypedTaggedEventStreamEnvelopeMailArchived = { + actor: string; + city: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.archived'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope mail.deleted + */ +export type TypedTaggedEventStreamEnvelopeMailDeleted = { + actor: string; + city: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.deleted'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope mail.marked_read + */ +export type TypedTaggedEventStreamEnvelopeMailMarkedRead = { + actor: string; + city: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.marked_read'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope mail.marked_unread + */ +export type TypedTaggedEventStreamEnvelopeMailMarkedUnread = { + actor: string; + city: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.marked_unread'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope mail.read + */ +export type TypedTaggedEventStreamEnvelopeMailRead = { + actor: string; + city: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.read'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope mail.replied + */ +export type TypedTaggedEventStreamEnvelopeMailReplied = { + actor: string; + city: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.replied'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope mail.sent + */ +export type TypedTaggedEventStreamEnvelopeMailSent = { + actor: string; + city: string; + message?: string; + payload: MailEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'mail.sent'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope order.completed + */ +export type TypedTaggedEventStreamEnvelopeOrderCompleted = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'order.completed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope order.failed + */ +export type TypedTaggedEventStreamEnvelopeOrderFailed = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'order.failed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope order.fired + */ +export type TypedTaggedEventStreamEnvelopeOrderFired = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'order.fired'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope provider.swapped + */ +export type TypedTaggedEventStreamEnvelopeProviderSwapped = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'provider.swapped'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope session.crashed + */ +export type TypedTaggedEventStreamEnvelopeSessionCrashed = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.crashed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope session.draining + */ +export type TypedTaggedEventStreamEnvelopeSessionDraining = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.draining'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope session.idle_killed + */ +export type TypedTaggedEventStreamEnvelopeSessionIdleKilled = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.idle_killed'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope session.quarantined + */ +export type TypedTaggedEventStreamEnvelopeSessionQuarantined = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.quarantined'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope session.stopped + */ +export type TypedTaggedEventStreamEnvelopeSessionStopped = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.stopped'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope session.suspended + */ +export type TypedTaggedEventStreamEnvelopeSessionSuspended = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.suspended'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope session.undrained + */ +export type TypedTaggedEventStreamEnvelopeSessionUndrained = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.undrained'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope session.updated + */ +export type TypedTaggedEventStreamEnvelopeSessionUpdated = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.updated'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope session.woke + */ +export type TypedTaggedEventStreamEnvelopeSessionWoke = { + actor: string; + city: string; + message?: string; + payload: NoPayload; + seq: number; + subject?: string; + ts: string; + type: 'session.woke'; + workflow?: WorkflowEventProjection; +}; + +/** + * TypedTaggedEventStreamEnvelope worker.operation + */ +export type TypedTaggedEventStreamEnvelopeWorkerOperation = { + actor: string; + city: string; + message?: string; + payload: WorkerOperationEventPayload; + seq: number; + subject?: string; + ts: string; + type: 'worker.operation'; + workflow?: WorkflowEventProjection; +}; + +export type UnboundEventPayload = { + count: number; + session_id: string; +}; + +export type WireEvent = { + actor: string; + message?: string; + payload?: EventPayload; + seq: number; + subject?: string; + ts: string; + type: string; +}; + +export type WireTaggedEvent = { + actor: string; + city: string; + message?: string; + payload?: EventPayload; + seq: number; + subject?: string; + ts: string; + type: string; +}; + +export type WorkerOperationEventPayload = { + delivered?: boolean; + duration_ms: number; + error?: string; + finished_at: string; + op_id: string; + operation: string; + provider?: string; + queued?: boolean; + result: string; + session_id?: string; + session_name?: string; + started_at: string; + template?: string; + transport?: string; +}; + +export type WorkflowAttemptSummary = { + active_attempt: number; + attempt_count: number; + max_attempts?: number; +}; + +export type WorkflowBeadResponse = { + assignee?: string; + attempt?: number; + id: string; + kind: string; + logical_bead_id?: string; + metadata: { + [key: string]: string; + }; + scope_ref?: string; + status: string; + step_ref?: string; + title: string; +}; + +export type WorkflowDeleteResponse = { + /** + * Number of beads closed. + */ + closed: number; + /** + * Number of beads deleted. + */ + deleted: number; + /** + * True when one or more teardown steps failed; Closed/Deleted still reflect what succeeded. + */ + partial?: boolean; + /** + * Human-readable errors from failed teardown steps. + */ + partial_errors?: Array | null; + /** + * Workflow ID. + */ + workflow_id: string; +}; + +export type WorkflowDepResponse = { + from: string; + kind?: string; + to: string; +}; + +export type WorkflowEventProjection = { + attempt_summary?: WorkflowAttemptSummary; + bead: WorkflowBeadResponse; + changed_fields: Array | null; + event_seq: number; + event_ts: string; + event_type: string; + logical_node_id: string; + requires_resync?: boolean; + root_bead_id: string; + root_store_ref: string; + scope_kind: string; + scope_ref: string; + type: string; + watch_generation: string; + workflow_id: string; + workflow_seq: number; +}; + +export type WorkflowSnapshotResponse = { + beads: Array | null; + deps: Array | null; + logical_edges: Array | null; + logical_nodes: Array | null; + partial: boolean; + resolved_root_store: string; + root_bead_id: string; + root_store_ref: string; + scope_groups: Array | null; + scope_kind: string; + scope_ref: string; + snapshot_event_seq?: number; + snapshot_version: number; + stores_scanned: Array | null; + workflow_id: string; +}; + +export type WorkspaceResponse = { + declared_name?: string; + declared_prefix?: string; + name: string; + prefix?: string; + provider?: string; + session_template?: string; + suspended: boolean; +}; + +export type GetHealthData = { + body?: never; + path?: never; + query?: never; + url: '/health'; +}; + +export type GetHealthErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetHealthError = GetHealthErrors[keyof GetHealthErrors]; + +export type GetHealthResponses = { + /** + * OK + */ + 200: SupervisorHealthOutputBody; +}; + +export type GetHealthResponse = GetHealthResponses[keyof GetHealthResponses]; + +export type GetV0CitiesData = { + body?: never; + path?: never; + query?: never; + url: '/v0/cities'; +}; + +export type GetV0CitiesErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CitiesError = GetV0CitiesErrors[keyof GetV0CitiesErrors]; + +export type GetV0CitiesResponses = { + /** + * OK + */ + 200: SupervisorCitiesOutputBody; +}; + +export type GetV0CitiesResponse = GetV0CitiesResponses[keyof GetV0CitiesResponses]; + +export type PostV0CityData = { + body: CityCreateRequest; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path?: never; + query?: never; + url: '/v0/city'; +}; + +export type PostV0CityErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityError = PostV0CityErrors[keyof PostV0CityErrors]; + +export type PostV0CityResponses = { + /** + * Accepted + */ + 202: CityCreateResponse; +}; + +export type PostV0CityResponse = PostV0CityResponses[keyof PostV0CityResponses]; + +export type GetV0CityByCityNameData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}'; +}; + +export type GetV0CityByCityNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameError = GetV0CityByCityNameErrors[keyof GetV0CityByCityNameErrors]; + +export type GetV0CityByCityNameResponses = { + /** + * OK + */ + 200: CityGetResponse; +}; + +export type GetV0CityByCityNameResponse = GetV0CityByCityNameResponses[keyof GetV0CityByCityNameResponses]; + +export type PatchV0CityByCityNameData = { + body: CityPatchInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}'; +}; + +export type PatchV0CityByCityNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PatchV0CityByCityNameError = PatchV0CityByCityNameErrors[keyof PatchV0CityByCityNameErrors]; + +export type PatchV0CityByCityNameResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PatchV0CityByCityNameResponse = PatchV0CityByCityNameResponses[keyof PatchV0CityByCityNameResponses]; + +export type DeleteV0CityByCityNameAgentByBaseData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent name (unqualified). + */ + base: string; + }; + query?: never; + url: '/v0/city/{cityName}/agent/{base}'; +}; + +export type DeleteV0CityByCityNameAgentByBaseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNameAgentByBaseError = DeleteV0CityByCityNameAgentByBaseErrors[keyof DeleteV0CityByCityNameAgentByBaseErrors]; + +export type DeleteV0CityByCityNameAgentByBaseResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type DeleteV0CityByCityNameAgentByBaseResponse = DeleteV0CityByCityNameAgentByBaseResponses[keyof DeleteV0CityByCityNameAgentByBaseResponses]; + +export type GetV0CityByCityNameAgentByBaseData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent name (unqualified, no rig). + */ + base: string; + }; + query?: never; + url: '/v0/city/{cityName}/agent/{base}'; +}; + +export type GetV0CityByCityNameAgentByBaseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameAgentByBaseError = GetV0CityByCityNameAgentByBaseErrors[keyof GetV0CityByCityNameAgentByBaseErrors]; + +export type GetV0CityByCityNameAgentByBaseResponses = { + /** + * OK + */ + 200: AgentResponse; +}; + +export type GetV0CityByCityNameAgentByBaseResponse = GetV0CityByCityNameAgentByBaseResponses[keyof GetV0CityByCityNameAgentByBaseResponses]; + +export type PatchV0CityByCityNameAgentByBaseData = { + body: AgentUpdateInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent name (unqualified). + */ + base: string; + }; + query?: never; + url: '/v0/city/{cityName}/agent/{base}'; +}; + +export type PatchV0CityByCityNameAgentByBaseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PatchV0CityByCityNameAgentByBaseError = PatchV0CityByCityNameAgentByBaseErrors[keyof PatchV0CityByCityNameAgentByBaseErrors]; + +export type PatchV0CityByCityNameAgentByBaseResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PatchV0CityByCityNameAgentByBaseResponse = PatchV0CityByCityNameAgentByBaseResponses[keyof PatchV0CityByCityNameAgentByBaseResponses]; + +export type GetV0CityByCityNameAgentByBaseOutputData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent base name. + */ + base: string; + }; + query?: { + /** + * Number of recent compaction segments to return. This API parameter keeps compaction-segment semantics even though gc session logs --tail counts displayed transcript entries. Omit for the endpoint default (usually 1); 0 returns all segments; N>0 returns the last N. + */ + tail?: string; + /** + * Message UUID cursor for loading older messages. + */ + before?: string; + }; + url: '/v0/city/{cityName}/agent/{base}/output'; +}; + +export type GetV0CityByCityNameAgentByBaseOutputErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameAgentByBaseOutputError = GetV0CityByCityNameAgentByBaseOutputErrors[keyof GetV0CityByCityNameAgentByBaseOutputErrors]; + +export type GetV0CityByCityNameAgentByBaseOutputResponses = { + /** + * OK + */ + 200: AgentOutputResponse; +}; + +export type GetV0CityByCityNameAgentByBaseOutputResponse = GetV0CityByCityNameAgentByBaseOutputResponses[keyof GetV0CityByCityNameAgentByBaseOutputResponses]; + +export type StreamAgentOutputData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent base name. + */ + base: string; + }; + query?: never; + url: '/v0/city/{cityName}/agent/{base}/output/stream'; +}; + +export type StreamAgentOutputErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type StreamAgentOutputError = StreamAgentOutputErrors[keyof StreamAgentOutputErrors]; + +export type StreamAgentOutputResponses = { + /** + * Server Sent Events + * + * Each oneOf object represents one possible SSE message. + */ + 200: Array<{ + data: HeartbeatEvent; + /** + * The event name. + */ + event: 'heartbeat'; + /** + * The event ID. + */ + id?: number; + /** + * The retry time in milliseconds. + */ + retry?: number; + } | { + data: AgentOutputResponse; + /** + * The event name. + */ + event: 'turn'; + /** + * The event ID. + */ + id?: number; + /** + * The retry time in milliseconds. + */ + retry?: number; + }>; +}; + +export type StreamAgentOutputResponse = StreamAgentOutputResponses[keyof StreamAgentOutputResponses]; + +export type PostV0CityByCityNameAgentByBaseByActionData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent name (unqualified). + */ + base: string; + /** + * Action to perform. + */ + action: 'suspend' | 'resume'; + }; + query?: never; + url: '/v0/city/{cityName}/agent/{base}/{action}'; +}; + +export type PostV0CityByCityNameAgentByBaseByActionErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameAgentByBaseByActionError = PostV0CityByCityNameAgentByBaseByActionErrors[keyof PostV0CityByCityNameAgentByBaseByActionErrors]; + +export type PostV0CityByCityNameAgentByBaseByActionResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameAgentByBaseByActionResponse = PostV0CityByCityNameAgentByBaseByActionResponses[keyof PostV0CityByCityNameAgentByBaseByActionResponses]; + +export type DeleteV0CityByCityNameAgentByDirByBaseData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent directory (rig name). + */ + dir: string; + /** + * Agent base name. + */ + base: string; + }; + query?: never; + url: '/v0/city/{cityName}/agent/{dir}/{base}'; +}; + +export type DeleteV0CityByCityNameAgentByDirByBaseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNameAgentByDirByBaseError = DeleteV0CityByCityNameAgentByDirByBaseErrors[keyof DeleteV0CityByCityNameAgentByDirByBaseErrors]; + +export type DeleteV0CityByCityNameAgentByDirByBaseResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type DeleteV0CityByCityNameAgentByDirByBaseResponse = DeleteV0CityByCityNameAgentByDirByBaseResponses[keyof DeleteV0CityByCityNameAgentByDirByBaseResponses]; + +export type GetV0CityByCityNameAgentByDirByBaseData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent directory (rig name). + */ + dir: string; + /** + * Agent base name. + */ + base: string; + }; + query?: never; + url: '/v0/city/{cityName}/agent/{dir}/{base}'; +}; + +export type GetV0CityByCityNameAgentByDirByBaseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameAgentByDirByBaseError = GetV0CityByCityNameAgentByDirByBaseErrors[keyof GetV0CityByCityNameAgentByDirByBaseErrors]; + +export type GetV0CityByCityNameAgentByDirByBaseResponses = { + /** + * OK + */ + 200: AgentResponse; +}; + +export type GetV0CityByCityNameAgentByDirByBaseResponse = GetV0CityByCityNameAgentByDirByBaseResponses[keyof GetV0CityByCityNameAgentByDirByBaseResponses]; + +export type PatchV0CityByCityNameAgentByDirByBaseData = { + body: AgentUpdateQualifiedInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent directory (rig name). + */ + dir: string; + /** + * Agent base name. + */ + base: string; + }; + query?: never; + url: '/v0/city/{cityName}/agent/{dir}/{base}'; +}; + +export type PatchV0CityByCityNameAgentByDirByBaseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PatchV0CityByCityNameAgentByDirByBaseError = PatchV0CityByCityNameAgentByDirByBaseErrors[keyof PatchV0CityByCityNameAgentByDirByBaseErrors]; + +export type PatchV0CityByCityNameAgentByDirByBaseResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PatchV0CityByCityNameAgentByDirByBaseResponse = PatchV0CityByCityNameAgentByDirByBaseResponses[keyof PatchV0CityByCityNameAgentByDirByBaseResponses]; + +export type GetV0CityByCityNameAgentByDirByBaseOutputData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent directory (rig name). + */ + dir: string; + /** + * Agent base name. + */ + base: string; + }; + query?: { + /** + * Number of recent compaction segments to return. This API parameter keeps compaction-segment semantics even though gc session logs --tail counts displayed transcript entries. Omit for the endpoint default (usually 1); 0 returns all segments; N>0 returns the last N. + */ + tail?: string; + /** + * Message UUID cursor for loading older messages. + */ + before?: string; + }; + url: '/v0/city/{cityName}/agent/{dir}/{base}/output'; +}; + +export type GetV0CityByCityNameAgentByDirByBaseOutputErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameAgentByDirByBaseOutputError = GetV0CityByCityNameAgentByDirByBaseOutputErrors[keyof GetV0CityByCityNameAgentByDirByBaseOutputErrors]; + +export type GetV0CityByCityNameAgentByDirByBaseOutputResponses = { + /** + * OK + */ + 200: AgentOutputResponse; +}; + +export type GetV0CityByCityNameAgentByDirByBaseOutputResponse = GetV0CityByCityNameAgentByDirByBaseOutputResponses[keyof GetV0CityByCityNameAgentByDirByBaseOutputResponses]; + +export type StreamAgentOutputQualifiedData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent directory (rig name). + */ + dir: string; + /** + * Agent base name. + */ + base: string; + }; + query?: never; + url: '/v0/city/{cityName}/agent/{dir}/{base}/output/stream'; +}; + +export type StreamAgentOutputQualifiedErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type StreamAgentOutputQualifiedError = StreamAgentOutputQualifiedErrors[keyof StreamAgentOutputQualifiedErrors]; + +export type StreamAgentOutputQualifiedResponses = { + /** + * Server Sent Events + * + * Each oneOf object represents one possible SSE message. + */ + 200: Array<{ + data: HeartbeatEvent; + /** + * The event name. + */ + event: 'heartbeat'; + /** + * The event ID. + */ + id?: number; + /** + * The retry time in milliseconds. + */ + retry?: number; + } | { + data: AgentOutputResponse; + /** + * The event name. + */ + event: 'turn'; + /** + * The event ID. + */ + id?: number; + /** + * The retry time in milliseconds. + */ + retry?: number; + }>; +}; + +export type StreamAgentOutputQualifiedResponse = StreamAgentOutputQualifiedResponses[keyof StreamAgentOutputQualifiedResponses]; + +export type PostV0CityByCityNameAgentByDirByBaseByActionData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent directory (rig name). + */ + dir: string; + /** + * Agent base name. + */ + base: string; + /** + * Action to perform. + */ + action: 'suspend' | 'resume'; + }; + query?: never; + url: '/v0/city/{cityName}/agent/{dir}/{base}/{action}'; +}; + +export type PostV0CityByCityNameAgentByDirByBaseByActionErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameAgentByDirByBaseByActionError = PostV0CityByCityNameAgentByDirByBaseByActionErrors[keyof PostV0CityByCityNameAgentByDirByBaseByActionErrors]; + +export type PostV0CityByCityNameAgentByDirByBaseByActionResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameAgentByDirByBaseByActionResponse = PostV0CityByCityNameAgentByDirByBaseByActionResponses[keyof PostV0CityByCityNameAgentByDirByBaseByActionResponses]; + +export type GetV0CityByCityNameAgentsData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Event sequence number; when provided, blocks until a newer event arrives. + */ + index?: string; + /** + * How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + */ + wait?: string; + /** + * Filter by pool name. + */ + pool?: string; + /** + * Filter by rig name. + */ + rig?: string; + /** + * Filter by running state. Omit to return all agents. + */ + running?: 'true' | 'false'; + /** + * Include last output preview. + */ + peek?: boolean; + }; + url: '/v0/city/{cityName}/agents'; +}; + +export type GetV0CityByCityNameAgentsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameAgentsError = GetV0CityByCityNameAgentsErrors[keyof GetV0CityByCityNameAgentsErrors]; + +export type GetV0CityByCityNameAgentsResponses = { + /** + * OK + */ + 200: ListBodyAgentResponse; +}; + +export type GetV0CityByCityNameAgentsResponse = GetV0CityByCityNameAgentsResponses[keyof GetV0CityByCityNameAgentsResponses]; + +export type CreateAgentData = { + body: AgentCreateInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/agents'; +}; + +export type CreateAgentErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type CreateAgentError = CreateAgentErrors[keyof CreateAgentErrors]; + +export type CreateAgentResponses = { + /** + * Created + */ + 201: AgentCreatedOutputBody; +}; + +export type CreateAgentResponse = CreateAgentResponses[keyof CreateAgentResponses]; + +export type DeleteV0CityByCityNameBeadByIdData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Bead ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/bead/{id}'; +}; + +export type DeleteV0CityByCityNameBeadByIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNameBeadByIdError = DeleteV0CityByCityNameBeadByIdErrors[keyof DeleteV0CityByCityNameBeadByIdErrors]; + +export type DeleteV0CityByCityNameBeadByIdResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type DeleteV0CityByCityNameBeadByIdResponse = DeleteV0CityByCityNameBeadByIdResponses[keyof DeleteV0CityByCityNameBeadByIdResponses]; + +export type GetV0CityByCityNameBeadByIdData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Bead ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/bead/{id}'; +}; + +export type GetV0CityByCityNameBeadByIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameBeadByIdError = GetV0CityByCityNameBeadByIdErrors[keyof GetV0CityByCityNameBeadByIdErrors]; + +export type GetV0CityByCityNameBeadByIdResponses = { + /** + * OK + */ + 200: Bead; +}; + +export type GetV0CityByCityNameBeadByIdResponse = GetV0CityByCityNameBeadByIdResponses[keyof GetV0CityByCityNameBeadByIdResponses]; + +export type PatchV0CityByCityNameBeadByIdData = { + body: BeadUpdateBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Bead ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/bead/{id}'; +}; + +export type PatchV0CityByCityNameBeadByIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PatchV0CityByCityNameBeadByIdError = PatchV0CityByCityNameBeadByIdErrors[keyof PatchV0CityByCityNameBeadByIdErrors]; + +export type PatchV0CityByCityNameBeadByIdResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PatchV0CityByCityNameBeadByIdResponse = PatchV0CityByCityNameBeadByIdResponses[keyof PatchV0CityByCityNameBeadByIdResponses]; + +export type PostV0CityByCityNameBeadByIdAssignData = { + body: BeadAssignInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Bead ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/bead/{id}/assign'; +}; + +export type PostV0CityByCityNameBeadByIdAssignErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameBeadByIdAssignError = PostV0CityByCityNameBeadByIdAssignErrors[keyof PostV0CityByCityNameBeadByIdAssignErrors]; + +export type PostV0CityByCityNameBeadByIdAssignResponses = { + /** + * OK + */ + 200: { + [key: string]: string; + }; +}; + +export type PostV0CityByCityNameBeadByIdAssignResponse = PostV0CityByCityNameBeadByIdAssignResponses[keyof PostV0CityByCityNameBeadByIdAssignResponses]; + +export type PostV0CityByCityNameBeadByIdCloseData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Bead ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/bead/{id}/close'; +}; + +export type PostV0CityByCityNameBeadByIdCloseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameBeadByIdCloseError = PostV0CityByCityNameBeadByIdCloseErrors[keyof PostV0CityByCityNameBeadByIdCloseErrors]; + +export type PostV0CityByCityNameBeadByIdCloseResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameBeadByIdCloseResponse = PostV0CityByCityNameBeadByIdCloseResponses[keyof PostV0CityByCityNameBeadByIdCloseResponses]; + +export type GetV0CityByCityNameBeadByIdDepsData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Bead ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/bead/{id}/deps'; +}; + +export type GetV0CityByCityNameBeadByIdDepsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameBeadByIdDepsError = GetV0CityByCityNameBeadByIdDepsErrors[keyof GetV0CityByCityNameBeadByIdDepsErrors]; + +export type GetV0CityByCityNameBeadByIdDepsResponses = { + /** + * OK + */ + 200: BeadDepsResponse; +}; + +export type GetV0CityByCityNameBeadByIdDepsResponse = GetV0CityByCityNameBeadByIdDepsResponses[keyof GetV0CityByCityNameBeadByIdDepsResponses]; + +export type PostV0CityByCityNameBeadByIdReopenData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Bead ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/bead/{id}/reopen'; +}; + +export type PostV0CityByCityNameBeadByIdReopenErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameBeadByIdReopenError = PostV0CityByCityNameBeadByIdReopenErrors[keyof PostV0CityByCityNameBeadByIdReopenErrors]; + +export type PostV0CityByCityNameBeadByIdReopenResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameBeadByIdReopenResponse = PostV0CityByCityNameBeadByIdReopenResponses[keyof PostV0CityByCityNameBeadByIdReopenResponses]; + +export type PostV0CityByCityNameBeadByIdUpdateData = { + body: BeadUpdateBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Bead ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/bead/{id}/update'; +}; + +export type PostV0CityByCityNameBeadByIdUpdateErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameBeadByIdUpdateError = PostV0CityByCityNameBeadByIdUpdateErrors[keyof PostV0CityByCityNameBeadByIdUpdateErrors]; + +export type PostV0CityByCityNameBeadByIdUpdateResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameBeadByIdUpdateResponse = PostV0CityByCityNameBeadByIdUpdateResponses[keyof PostV0CityByCityNameBeadByIdUpdateResponses]; + +export type GetV0CityByCityNameBeadsData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Event sequence number; when provided, blocks until a newer event arrives. + */ + index?: string; + /** + * How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + */ + wait?: string; + /** + * Pagination cursor from a previous response's next_cursor field. + */ + cursor?: string; + /** + * Maximum number of results to return. 0 = server default. + */ + limit?: number; + /** + * Filter by bead status. + */ + status?: string; + /** + * Filter by bead type. + */ + type?: string; + /** + * Filter by label. + */ + label?: string; + /** + * Filter by assignee. + */ + assignee?: string; + /** + * Filter by rig. + */ + rig?: string; + }; + url: '/v0/city/{cityName}/beads'; +}; + +export type GetV0CityByCityNameBeadsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameBeadsError = GetV0CityByCityNameBeadsErrors[keyof GetV0CityByCityNameBeadsErrors]; + +export type GetV0CityByCityNameBeadsResponses = { + /** + * OK + */ + 200: ListBodyBead; +}; + +export type GetV0CityByCityNameBeadsResponse = GetV0CityByCityNameBeadsResponses[keyof GetV0CityByCityNameBeadsResponses]; + +export type CreateBeadData = { + body: BeadCreateInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + /** + * Idempotency key for safe retries. + */ + 'Idempotency-Key'?: string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/beads'; +}; + +export type CreateBeadErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type CreateBeadError = CreateBeadErrors[keyof CreateBeadErrors]; + +export type CreateBeadResponses = { + /** + * Created + */ + 201: Bead; +}; + +export type CreateBeadResponse = CreateBeadResponses[keyof CreateBeadResponses]; + +export type GetV0CityByCityNameBeadsGraphByRootIdData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Root bead ID for the graph. + */ + rootID: string; + }; + query?: never; + url: '/v0/city/{cityName}/beads/graph/{rootID}'; +}; + +export type GetV0CityByCityNameBeadsGraphByRootIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameBeadsGraphByRootIdError = GetV0CityByCityNameBeadsGraphByRootIdErrors[keyof GetV0CityByCityNameBeadsGraphByRootIdErrors]; + +export type GetV0CityByCityNameBeadsGraphByRootIdResponses = { + /** + * OK + */ + 200: BeadGraphResponse; +}; + +export type GetV0CityByCityNameBeadsGraphByRootIdResponse = GetV0CityByCityNameBeadsGraphByRootIdResponses[keyof GetV0CityByCityNameBeadsGraphByRootIdResponses]; + +export type GetV0CityByCityNameBeadsReadyData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Event sequence number; when provided, blocks until a newer event arrives. + */ + index?: string; + /** + * How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + */ + wait?: string; + }; + url: '/v0/city/{cityName}/beads/ready'; +}; + +export type GetV0CityByCityNameBeadsReadyErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameBeadsReadyError = GetV0CityByCityNameBeadsReadyErrors[keyof GetV0CityByCityNameBeadsReadyErrors]; + +export type GetV0CityByCityNameBeadsReadyResponses = { + /** + * OK + */ + 200: ListBodyBead; +}; + +export type GetV0CityByCityNameBeadsReadyResponse = GetV0CityByCityNameBeadsReadyResponses[keyof GetV0CityByCityNameBeadsReadyResponses]; + +export type GetV0CityByCityNameConfigData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/config'; +}; + +export type GetV0CityByCityNameConfigErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameConfigError = GetV0CityByCityNameConfigErrors[keyof GetV0CityByCityNameConfigErrors]; + +export type GetV0CityByCityNameConfigResponses = { + /** + * OK + */ + 200: ConfigResponse; +}; + +export type GetV0CityByCityNameConfigResponse = GetV0CityByCityNameConfigResponses[keyof GetV0CityByCityNameConfigResponses]; + +export type GetV0CityByCityNameConfigExplainData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/config/explain'; +}; + +export type GetV0CityByCityNameConfigExplainErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameConfigExplainError = GetV0CityByCityNameConfigExplainErrors[keyof GetV0CityByCityNameConfigExplainErrors]; + +export type GetV0CityByCityNameConfigExplainResponses = { + /** + * OK + */ + 200: ConfigExplainResponse; +}; + +export type GetV0CityByCityNameConfigExplainResponse = GetV0CityByCityNameConfigExplainResponses[keyof GetV0CityByCityNameConfigExplainResponses]; + +export type GetV0CityByCityNameConfigValidateData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/config/validate'; +}; + +export type GetV0CityByCityNameConfigValidateErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameConfigValidateError = GetV0CityByCityNameConfigValidateErrors[keyof GetV0CityByCityNameConfigValidateErrors]; + +export type GetV0CityByCityNameConfigValidateResponses = { + /** + * OK + */ + 200: ConfigValidateOutputBody; +}; + +export type GetV0CityByCityNameConfigValidateResponse = GetV0CityByCityNameConfigValidateResponses[keyof GetV0CityByCityNameConfigValidateResponses]; + +export type DeleteV0CityByCityNameConvoyByIdData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Convoy ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/convoy/{id}'; +}; + +export type DeleteV0CityByCityNameConvoyByIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNameConvoyByIdError = DeleteV0CityByCityNameConvoyByIdErrors[keyof DeleteV0CityByCityNameConvoyByIdErrors]; + +export type DeleteV0CityByCityNameConvoyByIdResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type DeleteV0CityByCityNameConvoyByIdResponse = DeleteV0CityByCityNameConvoyByIdResponses[keyof DeleteV0CityByCityNameConvoyByIdResponses]; + +export type GetV0CityByCityNameConvoyByIdData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Convoy ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/convoy/{id}'; +}; + +export type GetV0CityByCityNameConvoyByIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameConvoyByIdError = GetV0CityByCityNameConvoyByIdErrors[keyof GetV0CityByCityNameConvoyByIdErrors]; + +export type GetV0CityByCityNameConvoyByIdResponses = { + /** + * OK + */ + 200: ConvoyGetResponse; +}; + +export type GetV0CityByCityNameConvoyByIdResponse = GetV0CityByCityNameConvoyByIdResponses[keyof GetV0CityByCityNameConvoyByIdResponses]; + +export type PostV0CityByCityNameConvoyByIdAddData = { + body: ConvoyAddInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Convoy ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/convoy/{id}/add'; +}; + +export type PostV0CityByCityNameConvoyByIdAddErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameConvoyByIdAddError = PostV0CityByCityNameConvoyByIdAddErrors[keyof PostV0CityByCityNameConvoyByIdAddErrors]; + +export type PostV0CityByCityNameConvoyByIdAddResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameConvoyByIdAddResponse = PostV0CityByCityNameConvoyByIdAddResponses[keyof PostV0CityByCityNameConvoyByIdAddResponses]; + +export type GetV0CityByCityNameConvoyByIdCheckData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Convoy ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/convoy/{id}/check'; +}; + +export type GetV0CityByCityNameConvoyByIdCheckErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameConvoyByIdCheckError = GetV0CityByCityNameConvoyByIdCheckErrors[keyof GetV0CityByCityNameConvoyByIdCheckErrors]; + +export type GetV0CityByCityNameConvoyByIdCheckResponses = { + /** + * OK + */ + 200: ConvoyCheckResponse; +}; + +export type GetV0CityByCityNameConvoyByIdCheckResponse = GetV0CityByCityNameConvoyByIdCheckResponses[keyof GetV0CityByCityNameConvoyByIdCheckResponses]; + +export type PostV0CityByCityNameConvoyByIdCloseData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Convoy ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/convoy/{id}/close'; +}; + +export type PostV0CityByCityNameConvoyByIdCloseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameConvoyByIdCloseError = PostV0CityByCityNameConvoyByIdCloseErrors[keyof PostV0CityByCityNameConvoyByIdCloseErrors]; + +export type PostV0CityByCityNameConvoyByIdCloseResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameConvoyByIdCloseResponse = PostV0CityByCityNameConvoyByIdCloseResponses[keyof PostV0CityByCityNameConvoyByIdCloseResponses]; + +export type PostV0CityByCityNameConvoyByIdRemoveData = { + body: ConvoyRemoveInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Convoy ID. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/convoy/{id}/remove'; +}; + +export type PostV0CityByCityNameConvoyByIdRemoveErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameConvoyByIdRemoveError = PostV0CityByCityNameConvoyByIdRemoveErrors[keyof PostV0CityByCityNameConvoyByIdRemoveErrors]; + +export type PostV0CityByCityNameConvoyByIdRemoveResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameConvoyByIdRemoveResponse = PostV0CityByCityNameConvoyByIdRemoveResponses[keyof PostV0CityByCityNameConvoyByIdRemoveResponses]; + +export type GetV0CityByCityNameConvoysData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Event sequence number; when provided, blocks until a newer event arrives. + */ + index?: string; + /** + * How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + */ + wait?: string; + /** + * Pagination cursor from a previous response's next_cursor field. + */ + cursor?: string; + /** + * Maximum number of results to return. 0 = server default. + */ + limit?: number; + }; + url: '/v0/city/{cityName}/convoys'; +}; + +export type GetV0CityByCityNameConvoysErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameConvoysError = GetV0CityByCityNameConvoysErrors[keyof GetV0CityByCityNameConvoysErrors]; + +export type GetV0CityByCityNameConvoysResponses = { + /** + * OK + */ + 200: ListBodyBead; +}; + +export type GetV0CityByCityNameConvoysResponse = GetV0CityByCityNameConvoysResponses[keyof GetV0CityByCityNameConvoysResponses]; + +export type CreateConvoyData = { + body: ConvoyCreateInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/convoys'; +}; + +export type CreateConvoyErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type CreateConvoyError = CreateConvoyErrors[keyof CreateConvoyErrors]; + +export type CreateConvoyResponses = { + /** + * Created + */ + 201: Bead; +}; + +export type CreateConvoyResponse = CreateConvoyResponses[keyof CreateConvoyResponses]; + +export type GetV0CityByCityNameEventsData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Event sequence number; when provided, blocks until a newer event arrives. + */ + index?: string; + /** + * How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + */ + wait?: string; + /** + * Pagination cursor from a previous response's next_cursor field. + */ + cursor?: string; + /** + * Maximum number of results to return. 0 = server default. + */ + limit?: number; + /** + * Filter by event type. + */ + type?: string; + /** + * Filter by actor. + */ + actor?: string; + /** + * Filter events since duration ago (Go duration string, e.g. 5m). + */ + since?: string; + }; + url: '/v0/city/{cityName}/events'; +}; + +export type GetV0CityByCityNameEventsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameEventsError = GetV0CityByCityNameEventsErrors[keyof GetV0CityByCityNameEventsErrors]; + +export type GetV0CityByCityNameEventsResponses = { + /** + * OK + */ + 200: ListBodyWireEvent; +}; + +export type GetV0CityByCityNameEventsResponse = GetV0CityByCityNameEventsResponses[keyof GetV0CityByCityNameEventsResponses]; + +export type EmitEventData = { + body: EventEmitRequest; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/events'; +}; + +export type EmitEventErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type EmitEventError = EmitEventErrors[keyof EmitEventErrors]; + +export type EmitEventResponses = { + /** + * Created + */ + 201: EventEmitOutputBody; +}; + +export type EmitEventResponse = EmitEventResponses[keyof EmitEventResponses]; + +export type StreamEventsData = { + body?: never; + headers?: { + /** + * SSE reconnect position from the last received event ID. + */ + 'Last-Event-ID'?: string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Reconnect position: only deliver events after this sequence number. + */ + after_seq?: string; + }; + url: '/v0/city/{cityName}/events/stream'; +}; + +export type StreamEventsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type StreamEventsError = StreamEventsErrors[keyof StreamEventsErrors]; + +export type StreamEventsResponses = { + /** + * Server Sent Events + * + * Each oneOf object represents one possible SSE message. + */ + 200: Array<{ + data: TypedEventStreamEnvelope; + /** + * The event name. + */ + event: 'event'; + /** + * The event ID. + */ + id?: number; + /** + * The retry time in milliseconds. + */ + retry?: number; + } | { + data: HeartbeatEvent; + /** + * The event name. + */ + event: 'heartbeat'; + /** + * The event ID. + */ + id?: number; + /** + * The retry time in milliseconds. + */ + retry?: number; + }>; +}; + +export type StreamEventsResponse = StreamEventsResponses[keyof StreamEventsResponses]; + +export type DeleteV0CityByCityNameExtmsgAdaptersData = { + body: ExtMsgAdapterUnregisterInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/extmsg/adapters'; +}; + +export type DeleteV0CityByCityNameExtmsgAdaptersErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNameExtmsgAdaptersError = DeleteV0CityByCityNameExtmsgAdaptersErrors[keyof DeleteV0CityByCityNameExtmsgAdaptersErrors]; + +export type DeleteV0CityByCityNameExtmsgAdaptersResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type DeleteV0CityByCityNameExtmsgAdaptersResponse = DeleteV0CityByCityNameExtmsgAdaptersResponses[keyof DeleteV0CityByCityNameExtmsgAdaptersResponses]; + +export type GetV0CityByCityNameExtmsgAdaptersData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/extmsg/adapters'; +}; + +export type GetV0CityByCityNameExtmsgAdaptersErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameExtmsgAdaptersError = GetV0CityByCityNameExtmsgAdaptersErrors[keyof GetV0CityByCityNameExtmsgAdaptersErrors]; + +export type GetV0CityByCityNameExtmsgAdaptersResponses = { + /** + * OK + */ + 200: ListBodyExtmsgAdapterInfo; +}; + +export type GetV0CityByCityNameExtmsgAdaptersResponse = GetV0CityByCityNameExtmsgAdaptersResponses[keyof GetV0CityByCityNameExtmsgAdaptersResponses]; + +export type RegisterExtmsgAdapterData = { + body: ExtMsgAdapterRegisterInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/extmsg/adapters'; +}; + +export type RegisterExtmsgAdapterErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type RegisterExtmsgAdapterError = RegisterExtmsgAdapterErrors[keyof RegisterExtmsgAdapterErrors]; + +export type RegisterExtmsgAdapterResponses = { + /** + * Created + */ + 201: ExtMsgAdapterRegisterOutputBody; +}; + +export type RegisterExtmsgAdapterResponse = RegisterExtmsgAdapterResponses[keyof RegisterExtmsgAdapterResponses]; + +export type PostV0CityByCityNameExtmsgBindData = { + body: ExtMsgBindInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/extmsg/bind'; +}; + +export type PostV0CityByCityNameExtmsgBindErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameExtmsgBindError = PostV0CityByCityNameExtmsgBindErrors[keyof PostV0CityByCityNameExtmsgBindErrors]; + +export type PostV0CityByCityNameExtmsgBindResponses = { + /** + * OK + */ + 200: SessionBindingRecord; +}; + +export type PostV0CityByCityNameExtmsgBindResponse = PostV0CityByCityNameExtmsgBindResponses[keyof PostV0CityByCityNameExtmsgBindResponses]; + +export type GetV0CityByCityNameExtmsgBindingsData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Session ID to list bindings for. + */ + session_id?: string; + }; + url: '/v0/city/{cityName}/extmsg/bindings'; +}; + +export type GetV0CityByCityNameExtmsgBindingsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameExtmsgBindingsError = GetV0CityByCityNameExtmsgBindingsErrors[keyof GetV0CityByCityNameExtmsgBindingsErrors]; + +export type GetV0CityByCityNameExtmsgBindingsResponses = { + /** + * OK + */ + 200: ListBodySessionBindingRecord; +}; + +export type GetV0CityByCityNameExtmsgBindingsResponse = GetV0CityByCityNameExtmsgBindingsResponses[keyof GetV0CityByCityNameExtmsgBindingsResponses]; + +export type GetV0CityByCityNameExtmsgGroupsData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Scope ID. + */ + scope_id?: string; + /** + * Provider name. + */ + provider?: string; + /** + * Account ID. + */ + account_id?: string; + /** + * Conversation ID. + */ + conversation_id?: string; + /** + * Conversation kind. + */ + kind?: string; + }; + url: '/v0/city/{cityName}/extmsg/groups'; +}; + +export type GetV0CityByCityNameExtmsgGroupsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameExtmsgGroupsError = GetV0CityByCityNameExtmsgGroupsErrors[keyof GetV0CityByCityNameExtmsgGroupsErrors]; + +export type GetV0CityByCityNameExtmsgGroupsResponses = { + /** + * OK + */ + 200: ConversationGroupRecord; +}; + +export type GetV0CityByCityNameExtmsgGroupsResponse = GetV0CityByCityNameExtmsgGroupsResponses[keyof GetV0CityByCityNameExtmsgGroupsResponses]; + +export type EnsureExtmsgGroupData = { + body: ExtMsgGroupEnsureInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/extmsg/groups'; +}; + +export type EnsureExtmsgGroupErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type EnsureExtmsgGroupError = EnsureExtmsgGroupErrors[keyof EnsureExtmsgGroupErrors]; + +export type EnsureExtmsgGroupResponses = { + /** + * Created + */ + 201: ConversationGroupRecord; +}; + +export type EnsureExtmsgGroupResponse = EnsureExtmsgGroupResponses[keyof EnsureExtmsgGroupResponses]; + +export type PostV0CityByCityNameExtmsgInboundData = { + body: ExtMsgInboundInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/extmsg/inbound'; +}; + +export type PostV0CityByCityNameExtmsgInboundErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameExtmsgInboundError = PostV0CityByCityNameExtmsgInboundErrors[keyof PostV0CityByCityNameExtmsgInboundErrors]; + +export type PostV0CityByCityNameExtmsgInboundResponses = { + /** + * OK + */ + 200: InboundResult; +}; + +export type PostV0CityByCityNameExtmsgInboundResponse = PostV0CityByCityNameExtmsgInboundResponses[keyof PostV0CityByCityNameExtmsgInboundResponses]; + +export type PostV0CityByCityNameExtmsgOutboundData = { + body: ExtMsgOutboundInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/extmsg/outbound'; +}; + +export type PostV0CityByCityNameExtmsgOutboundErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameExtmsgOutboundError = PostV0CityByCityNameExtmsgOutboundErrors[keyof PostV0CityByCityNameExtmsgOutboundErrors]; + +export type PostV0CityByCityNameExtmsgOutboundResponses = { + /** + * OK + */ + 200: OutboundResult; +}; + +export type PostV0CityByCityNameExtmsgOutboundResponse = PostV0CityByCityNameExtmsgOutboundResponses[keyof PostV0CityByCityNameExtmsgOutboundResponses]; + +export type DeleteV0CityByCityNameExtmsgParticipantsData = { + body: ExtMsgParticipantRemoveInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/extmsg/participants'; +}; + +export type DeleteV0CityByCityNameExtmsgParticipantsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNameExtmsgParticipantsError = DeleteV0CityByCityNameExtmsgParticipantsErrors[keyof DeleteV0CityByCityNameExtmsgParticipantsErrors]; + +export type DeleteV0CityByCityNameExtmsgParticipantsResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type DeleteV0CityByCityNameExtmsgParticipantsResponse = DeleteV0CityByCityNameExtmsgParticipantsResponses[keyof DeleteV0CityByCityNameExtmsgParticipantsResponses]; + +export type PostV0CityByCityNameExtmsgParticipantsData = { + body: ExtMsgParticipantUpsertInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/extmsg/participants'; +}; + +export type PostV0CityByCityNameExtmsgParticipantsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameExtmsgParticipantsError = PostV0CityByCityNameExtmsgParticipantsErrors[keyof PostV0CityByCityNameExtmsgParticipantsErrors]; + +export type PostV0CityByCityNameExtmsgParticipantsResponses = { + /** + * OK + */ + 200: ConversationGroupParticipant; +}; + +export type PostV0CityByCityNameExtmsgParticipantsResponse = PostV0CityByCityNameExtmsgParticipantsResponses[keyof PostV0CityByCityNameExtmsgParticipantsResponses]; + +export type GetV0CityByCityNameExtmsgTranscriptData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Scope ID. + */ + scope_id?: string; + /** + * Provider name. + */ + provider?: string; + /** + * Account ID. + */ + account_id?: string; + /** + * Conversation ID. + */ + conversation_id?: string; + /** + * Parent conversation ID. + */ + parent_conversation_id?: string; + /** + * Conversation kind. + */ + kind?: string; + }; + url: '/v0/city/{cityName}/extmsg/transcript'; +}; + +export type GetV0CityByCityNameExtmsgTranscriptErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameExtmsgTranscriptError = GetV0CityByCityNameExtmsgTranscriptErrors[keyof GetV0CityByCityNameExtmsgTranscriptErrors]; + +export type GetV0CityByCityNameExtmsgTranscriptResponses = { + /** + * OK + */ + 200: ListBodyConversationTranscriptRecord; +}; + +export type GetV0CityByCityNameExtmsgTranscriptResponse = GetV0CityByCityNameExtmsgTranscriptResponses[keyof GetV0CityByCityNameExtmsgTranscriptResponses]; + +export type PostV0CityByCityNameExtmsgTranscriptAckData = { + body: ExtMsgTranscriptAckInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/extmsg/transcript/ack'; +}; + +export type PostV0CityByCityNameExtmsgTranscriptAckErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameExtmsgTranscriptAckError = PostV0CityByCityNameExtmsgTranscriptAckErrors[keyof PostV0CityByCityNameExtmsgTranscriptAckErrors]; + +export type PostV0CityByCityNameExtmsgTranscriptAckResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameExtmsgTranscriptAckResponse = PostV0CityByCityNameExtmsgTranscriptAckResponses[keyof PostV0CityByCityNameExtmsgTranscriptAckResponses]; + +export type PostV0CityByCityNameExtmsgUnbindData = { + body: ExtMsgUnbindInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/extmsg/unbind'; +}; + +export type PostV0CityByCityNameExtmsgUnbindErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameExtmsgUnbindError = PostV0CityByCityNameExtmsgUnbindErrors[keyof PostV0CityByCityNameExtmsgUnbindErrors]; + +export type PostV0CityByCityNameExtmsgUnbindResponses = { + /** + * OK + */ + 200: ExtMsgUnbindBody; +}; + +export type PostV0CityByCityNameExtmsgUnbindResponse = PostV0CityByCityNameExtmsgUnbindResponses[keyof PostV0CityByCityNameExtmsgUnbindResponses]; + +export type GetV0CityByCityNameFormulaByNameData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Formula name. + */ + name: string; + }; + query: { + /** + * Scope kind (city or rig). + */ + scope_kind?: string; + /** + * Scope reference. + */ + scope_ref?: string; + /** + * Target agent for preview compilation. + */ + target: string; + }; + url: '/v0/city/{cityName}/formula/{name}'; +}; + +export type GetV0CityByCityNameFormulaByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameFormulaByNameError = GetV0CityByCityNameFormulaByNameErrors[keyof GetV0CityByCityNameFormulaByNameErrors]; + +export type GetV0CityByCityNameFormulaByNameResponses = { + /** + * OK + */ + 200: FormulaDetailResponse; +}; + +export type GetV0CityByCityNameFormulaByNameResponse = GetV0CityByCityNameFormulaByNameResponses[keyof GetV0CityByCityNameFormulaByNameResponses]; + +export type GetV0CityByCityNameFormulasData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Scope kind (city or rig). + */ + scope_kind?: string; + /** + * Scope reference. + */ + scope_ref?: string; + }; + url: '/v0/city/{cityName}/formulas'; +}; + +export type GetV0CityByCityNameFormulasErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameFormulasError = GetV0CityByCityNameFormulasErrors[keyof GetV0CityByCityNameFormulasErrors]; + +export type GetV0CityByCityNameFormulasResponses = { + /** + * OK + */ + 200: FormulaListBody; +}; + +export type GetV0CityByCityNameFormulasResponse = GetV0CityByCityNameFormulasResponses[keyof GetV0CityByCityNameFormulasResponses]; + +export type GetV0CityByCityNameFormulasFeedData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Scope kind (city or rig). + */ + scope_kind?: string; + /** + * Scope reference. + */ + scope_ref?: string; + /** + * Maximum number of feed items to return. 0 = default. + */ + limit?: number; + }; + url: '/v0/city/{cityName}/formulas/feed'; +}; + +export type GetV0CityByCityNameFormulasFeedErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameFormulasFeedError = GetV0CityByCityNameFormulasFeedErrors[keyof GetV0CityByCityNameFormulasFeedErrors]; + +export type GetV0CityByCityNameFormulasFeedResponses = { + /** + * OK + */ + 200: FormulaFeedBody; +}; + +export type GetV0CityByCityNameFormulasFeedResponse = GetV0CityByCityNameFormulasFeedResponses[keyof GetV0CityByCityNameFormulasFeedResponses]; + +export type GetV0CityByCityNameFormulasByNameData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Formula name. + */ + name: string; + }; + query: { + /** + * Scope kind (city or rig). + */ + scope_kind?: string; + /** + * Scope reference. + */ + scope_ref?: string; + /** + * Target agent for preview compilation. + */ + target: string; + }; + url: '/v0/city/{cityName}/formulas/{name}'; +}; + +export type GetV0CityByCityNameFormulasByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameFormulasByNameError = GetV0CityByCityNameFormulasByNameErrors[keyof GetV0CityByCityNameFormulasByNameErrors]; + +export type GetV0CityByCityNameFormulasByNameResponses = { + /** + * OK + */ + 200: FormulaDetailResponse; +}; + +export type GetV0CityByCityNameFormulasByNameResponse = GetV0CityByCityNameFormulasByNameResponses[keyof GetV0CityByCityNameFormulasByNameResponses]; + +export type PostV0CityByCityNameFormulasByNamePreviewData = { + body: FormulaPreviewBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Formula name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/formulas/{name}/preview'; +}; + +export type PostV0CityByCityNameFormulasByNamePreviewErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameFormulasByNamePreviewError = PostV0CityByCityNameFormulasByNamePreviewErrors[keyof PostV0CityByCityNameFormulasByNamePreviewErrors]; + +export type PostV0CityByCityNameFormulasByNamePreviewResponses = { + /** + * OK + */ + 200: FormulaDetailResponse; +}; + +export type PostV0CityByCityNameFormulasByNamePreviewResponse = PostV0CityByCityNameFormulasByNamePreviewResponses[keyof PostV0CityByCityNameFormulasByNamePreviewResponses]; + +export type GetV0CityByCityNameFormulasByNameRunsData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Formula name. + */ + name: string; + }; + query?: { + /** + * Scope kind (city or rig). + */ + scope_kind?: string; + /** + * Scope reference. + */ + scope_ref?: string; + /** + * Maximum number of recent runs to return. 0 = default. + */ + limit?: number; + }; + url: '/v0/city/{cityName}/formulas/{name}/runs'; +}; + +export type GetV0CityByCityNameFormulasByNameRunsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameFormulasByNameRunsError = GetV0CityByCityNameFormulasByNameRunsErrors[keyof GetV0CityByCityNameFormulasByNameRunsErrors]; + +export type GetV0CityByCityNameFormulasByNameRunsResponses = { + /** + * OK + */ + 200: FormulaRunsResponse; +}; + +export type GetV0CityByCityNameFormulasByNameRunsResponse = GetV0CityByCityNameFormulasByNameRunsResponses[keyof GetV0CityByCityNameFormulasByNameRunsResponses]; + +export type GetV0CityByCityNameHealthData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/health'; +}; + +export type GetV0CityByCityNameHealthErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameHealthError = GetV0CityByCityNameHealthErrors[keyof GetV0CityByCityNameHealthErrors]; + +export type GetV0CityByCityNameHealthResponses = { + /** + * OK + */ + 200: HealthOutputBody; +}; + +export type GetV0CityByCityNameHealthResponse = GetV0CityByCityNameHealthResponses[keyof GetV0CityByCityNameHealthResponses]; + +export type GetV0CityByCityNameMailData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Event sequence number; when provided, blocks until a newer event arrives. + */ + index?: string; + /** + * How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + */ + wait?: string; + /** + * Pagination cursor from a previous response's next_cursor field. + */ + cursor?: string; + /** + * Maximum number of results to return. 0 = server default. + */ + limit?: number; + /** + * Filter by agent name. + */ + agent?: string; + /** + * Filter by status (unread, all). + */ + status?: string; + /** + * Filter by rig name. + */ + rig?: string; + }; + url: '/v0/city/{cityName}/mail'; +}; + +export type GetV0CityByCityNameMailErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameMailError = GetV0CityByCityNameMailErrors[keyof GetV0CityByCityNameMailErrors]; + +export type GetV0CityByCityNameMailResponses = { + /** + * OK + */ + 200: MailListBody; +}; + +export type GetV0CityByCityNameMailResponse = GetV0CityByCityNameMailResponses[keyof GetV0CityByCityNameMailResponses]; + +export type SendMailData = { + body: MailSendInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + /** + * Idempotency key for safe retries. + */ + 'Idempotency-Key'?: string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/mail'; +}; + +export type SendMailErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type SendMailError = SendMailErrors[keyof SendMailErrors]; + +export type SendMailResponses = { + /** + * Created + */ + 201: Message; +}; + +export type SendMailResponse = SendMailResponses[keyof SendMailResponses]; + +export type GetV0CityByCityNameMailCountData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Filter by agent name. + */ + agent?: string; + /** + * Filter by rig name. + */ + rig?: string; + }; + url: '/v0/city/{cityName}/mail/count'; +}; + +export type GetV0CityByCityNameMailCountErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameMailCountError = GetV0CityByCityNameMailCountErrors[keyof GetV0CityByCityNameMailCountErrors]; + +export type GetV0CityByCityNameMailCountResponses = { + /** + * OK + */ + 200: MailCountOutputBody; +}; + +export type GetV0CityByCityNameMailCountResponse = GetV0CityByCityNameMailCountResponses[keyof GetV0CityByCityNameMailCountResponses]; + +export type GetV0CityByCityNameMailThreadByIdData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Thread ID. + */ + id: string; + }; + query?: { + /** + * Filter by rig. + */ + rig?: string; + }; + url: '/v0/city/{cityName}/mail/thread/{id}'; +}; + +export type GetV0CityByCityNameMailThreadByIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameMailThreadByIdError = GetV0CityByCityNameMailThreadByIdErrors[keyof GetV0CityByCityNameMailThreadByIdErrors]; + +export type GetV0CityByCityNameMailThreadByIdResponses = { + /** + * OK + */ + 200: MailListBody; +}; + +export type GetV0CityByCityNameMailThreadByIdResponse = GetV0CityByCityNameMailThreadByIdResponses[keyof GetV0CityByCityNameMailThreadByIdResponses]; + +export type DeleteV0CityByCityNameMailByIdData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Message ID. + */ + id: string; + }; + query?: { + /** + * Rig hint. + */ + rig?: string; + }; + url: '/v0/city/{cityName}/mail/{id}'; +}; + +export type DeleteV0CityByCityNameMailByIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNameMailByIdError = DeleteV0CityByCityNameMailByIdErrors[keyof DeleteV0CityByCityNameMailByIdErrors]; + +export type DeleteV0CityByCityNameMailByIdResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type DeleteV0CityByCityNameMailByIdResponse = DeleteV0CityByCityNameMailByIdResponses[keyof DeleteV0CityByCityNameMailByIdResponses]; + +export type GetV0CityByCityNameMailByIdData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Message ID. + */ + id: string; + }; + query?: { + /** + * Rig hint for O(1) lookup. + */ + rig?: string; + }; + url: '/v0/city/{cityName}/mail/{id}'; +}; + +export type GetV0CityByCityNameMailByIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameMailByIdError = GetV0CityByCityNameMailByIdErrors[keyof GetV0CityByCityNameMailByIdErrors]; + +export type GetV0CityByCityNameMailByIdResponses = { + /** + * OK + */ + 200: Message; +}; + +export type GetV0CityByCityNameMailByIdResponse = GetV0CityByCityNameMailByIdResponses[keyof GetV0CityByCityNameMailByIdResponses]; + +export type PostV0CityByCityNameMailByIdArchiveData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Message ID. + */ + id: string; + }; + query?: { + /** + * Rig hint. + */ + rig?: string; + }; + url: '/v0/city/{cityName}/mail/{id}/archive'; +}; + +export type PostV0CityByCityNameMailByIdArchiveErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameMailByIdArchiveError = PostV0CityByCityNameMailByIdArchiveErrors[keyof PostV0CityByCityNameMailByIdArchiveErrors]; + +export type PostV0CityByCityNameMailByIdArchiveResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameMailByIdArchiveResponse = PostV0CityByCityNameMailByIdArchiveResponses[keyof PostV0CityByCityNameMailByIdArchiveResponses]; + +export type PostV0CityByCityNameMailByIdMarkUnreadData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Message ID. + */ + id: string; + }; + query?: { + /** + * Rig hint. + */ + rig?: string; + }; + url: '/v0/city/{cityName}/mail/{id}/mark-unread'; +}; + +export type PostV0CityByCityNameMailByIdMarkUnreadErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameMailByIdMarkUnreadError = PostV0CityByCityNameMailByIdMarkUnreadErrors[keyof PostV0CityByCityNameMailByIdMarkUnreadErrors]; + +export type PostV0CityByCityNameMailByIdMarkUnreadResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameMailByIdMarkUnreadResponse = PostV0CityByCityNameMailByIdMarkUnreadResponses[keyof PostV0CityByCityNameMailByIdMarkUnreadResponses]; + +export type PostV0CityByCityNameMailByIdReadData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Message ID. + */ + id: string; + }; + query?: { + /** + * Rig hint. + */ + rig?: string; + }; + url: '/v0/city/{cityName}/mail/{id}/read'; +}; + +export type PostV0CityByCityNameMailByIdReadErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameMailByIdReadError = PostV0CityByCityNameMailByIdReadErrors[keyof PostV0CityByCityNameMailByIdReadErrors]; + +export type PostV0CityByCityNameMailByIdReadResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameMailByIdReadResponse = PostV0CityByCityNameMailByIdReadResponses[keyof PostV0CityByCityNameMailByIdReadResponses]; + +export type ReplyMailData = { + body: MailReplyInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Message ID. + */ + id: string; + }; + query?: { + /** + * Rig hint. + */ + rig?: string; + }; + url: '/v0/city/{cityName}/mail/{id}/reply'; +}; + +export type ReplyMailErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type ReplyMailError = ReplyMailErrors[keyof ReplyMailErrors]; + +export type ReplyMailResponses = { + /** + * Created + */ + 201: Message; +}; + +export type ReplyMailResponse = ReplyMailResponses[keyof ReplyMailResponses]; + +export type GetV0CityByCityNameOrderHistoryByBeadIdData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Bead ID for the order run. + */ + bead_id: string; + }; + query?: { + /** + * Store reference for disambiguating store-local bead IDs. + */ + store_ref?: string; + }; + url: '/v0/city/{cityName}/order/history/{bead_id}'; +}; + +export type GetV0CityByCityNameOrderHistoryByBeadIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameOrderHistoryByBeadIdError = GetV0CityByCityNameOrderHistoryByBeadIdErrors[keyof GetV0CityByCityNameOrderHistoryByBeadIdErrors]; + +export type GetV0CityByCityNameOrderHistoryByBeadIdResponses = { + /** + * OK + */ + 200: OrderHistoryDetailResponse; +}; + +export type GetV0CityByCityNameOrderHistoryByBeadIdResponse = GetV0CityByCityNameOrderHistoryByBeadIdResponses[keyof GetV0CityByCityNameOrderHistoryByBeadIdResponses]; + +export type GetV0CityByCityNameOrderByNameData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Order name or scoped name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/order/{name}'; +}; + +export type GetV0CityByCityNameOrderByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameOrderByNameError = GetV0CityByCityNameOrderByNameErrors[keyof GetV0CityByCityNameOrderByNameErrors]; + +export type GetV0CityByCityNameOrderByNameResponses = { + /** + * OK + */ + 200: OrderResponse; +}; + +export type GetV0CityByCityNameOrderByNameResponse = GetV0CityByCityNameOrderByNameResponses[keyof GetV0CityByCityNameOrderByNameResponses]; + +export type PostV0CityByCityNameOrderByNameDisableData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Order name or scoped name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/order/{name}/disable'; +}; + +export type PostV0CityByCityNameOrderByNameDisableErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameOrderByNameDisableError = PostV0CityByCityNameOrderByNameDisableErrors[keyof PostV0CityByCityNameOrderByNameDisableErrors]; + +export type PostV0CityByCityNameOrderByNameDisableResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameOrderByNameDisableResponse = PostV0CityByCityNameOrderByNameDisableResponses[keyof PostV0CityByCityNameOrderByNameDisableResponses]; + +export type PostV0CityByCityNameOrderByNameEnableData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Order name or scoped name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/order/{name}/enable'; +}; + +export type PostV0CityByCityNameOrderByNameEnableErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameOrderByNameEnableError = PostV0CityByCityNameOrderByNameEnableErrors[keyof PostV0CityByCityNameOrderByNameEnableErrors]; + +export type PostV0CityByCityNameOrderByNameEnableResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameOrderByNameEnableResponse = PostV0CityByCityNameOrderByNameEnableResponses[keyof PostV0CityByCityNameOrderByNameEnableResponses]; + +export type GetV0CityByCityNameOrdersData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/orders'; +}; + +export type GetV0CityByCityNameOrdersErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameOrdersError = GetV0CityByCityNameOrdersErrors[keyof GetV0CityByCityNameOrdersErrors]; + +export type GetV0CityByCityNameOrdersResponses = { + /** + * OK + */ + 200: OrderListBody; +}; + +export type GetV0CityByCityNameOrdersResponse = GetV0CityByCityNameOrdersResponses[keyof GetV0CityByCityNameOrdersResponses]; + +export type GetV0CityByCityNameOrdersCheckData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/orders/check'; +}; + +export type GetV0CityByCityNameOrdersCheckErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameOrdersCheckError = GetV0CityByCityNameOrdersCheckErrors[keyof GetV0CityByCityNameOrdersCheckErrors]; + +export type GetV0CityByCityNameOrdersCheckResponses = { + /** + * OK + */ + 200: OrderCheckListBody; +}; + +export type GetV0CityByCityNameOrdersCheckResponse = GetV0CityByCityNameOrdersCheckResponses[keyof GetV0CityByCityNameOrdersCheckResponses]; + +export type GetV0CityByCityNameOrdersFeedData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Scope kind (city or rig). + */ + scope_kind?: string; + /** + * Scope reference. + */ + scope_ref?: string; + /** + * Maximum number of feed items to return. + */ + limit?: number; + }; + url: '/v0/city/{cityName}/orders/feed'; +}; + +export type GetV0CityByCityNameOrdersFeedErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameOrdersFeedError = GetV0CityByCityNameOrdersFeedErrors[keyof GetV0CityByCityNameOrdersFeedErrors]; + +export type GetV0CityByCityNameOrdersFeedResponses = { + /** + * OK + */ + 200: OrdersFeedBody; +}; + +export type GetV0CityByCityNameOrdersFeedResponse = GetV0CityByCityNameOrdersFeedResponses[keyof GetV0CityByCityNameOrdersFeedResponses]; + +export type GetV0CityByCityNameOrdersHistoryData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query: { + /** + * Scoped order name. + */ + scoped_name: string; + /** + * Maximum number of history entries. 0 = default. + */ + limit?: number; + /** + * Return entries before this RFC3339 timestamp. + */ + before?: string; + }; + url: '/v0/city/{cityName}/orders/history'; +}; + +export type GetV0CityByCityNameOrdersHistoryErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameOrdersHistoryError = GetV0CityByCityNameOrdersHistoryErrors[keyof GetV0CityByCityNameOrdersHistoryErrors]; + +export type GetV0CityByCityNameOrdersHistoryResponses = { + /** + * OK + */ + 200: OrderHistoryListBody; +}; + +export type GetV0CityByCityNameOrdersHistoryResponse = GetV0CityByCityNameOrdersHistoryResponses[keyof GetV0CityByCityNameOrdersHistoryResponses]; + +export type GetV0CityByCityNamePacksData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/packs'; +}; + +export type GetV0CityByCityNamePacksErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNamePacksError = GetV0CityByCityNamePacksErrors[keyof GetV0CityByCityNamePacksErrors]; + +export type GetV0CityByCityNamePacksResponses = { + /** + * OK + */ + 200: PackListBody; +}; + +export type GetV0CityByCityNamePacksResponse = GetV0CityByCityNamePacksResponses[keyof GetV0CityByCityNamePacksResponses]; + +export type DeleteV0CityByCityNamePatchesAgentByBaseData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent patch name (unqualified). + */ + base: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/agent/{base}'; +}; + +export type DeleteV0CityByCityNamePatchesAgentByBaseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNamePatchesAgentByBaseError = DeleteV0CityByCityNamePatchesAgentByBaseErrors[keyof DeleteV0CityByCityNamePatchesAgentByBaseErrors]; + +export type DeleteV0CityByCityNamePatchesAgentByBaseResponses = { + /** + * OK + */ + 200: PatchDeletedResponseBody; +}; + +export type DeleteV0CityByCityNamePatchesAgentByBaseResponse = DeleteV0CityByCityNamePatchesAgentByBaseResponses[keyof DeleteV0CityByCityNamePatchesAgentByBaseResponses]; + +export type GetV0CityByCityNamePatchesAgentByBaseData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent patch name (unqualified). + */ + base: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/agent/{base}'; +}; + +export type GetV0CityByCityNamePatchesAgentByBaseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNamePatchesAgentByBaseError = GetV0CityByCityNamePatchesAgentByBaseErrors[keyof GetV0CityByCityNamePatchesAgentByBaseErrors]; + +export type GetV0CityByCityNamePatchesAgentByBaseResponses = { + /** + * OK + */ + 200: AgentPatch; +}; + +export type GetV0CityByCityNamePatchesAgentByBaseResponse = GetV0CityByCityNamePatchesAgentByBaseResponses[keyof GetV0CityByCityNamePatchesAgentByBaseResponses]; + +export type DeleteV0CityByCityNamePatchesAgentByDirByBaseData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent directory (rig name). + */ + dir: string; + /** + * Agent base name. + */ + base: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/agent/{dir}/{base}'; +}; + +export type DeleteV0CityByCityNamePatchesAgentByDirByBaseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNamePatchesAgentByDirByBaseError = DeleteV0CityByCityNamePatchesAgentByDirByBaseErrors[keyof DeleteV0CityByCityNamePatchesAgentByDirByBaseErrors]; + +export type DeleteV0CityByCityNamePatchesAgentByDirByBaseResponses = { + /** + * OK + */ + 200: PatchDeletedResponseBody; +}; + +export type DeleteV0CityByCityNamePatchesAgentByDirByBaseResponse = DeleteV0CityByCityNamePatchesAgentByDirByBaseResponses[keyof DeleteV0CityByCityNamePatchesAgentByDirByBaseResponses]; + +export type GetV0CityByCityNamePatchesAgentByDirByBaseData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Agent directory (rig name). + */ + dir: string; + /** + * Agent base name. + */ + base: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/agent/{dir}/{base}'; +}; + +export type GetV0CityByCityNamePatchesAgentByDirByBaseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNamePatchesAgentByDirByBaseError = GetV0CityByCityNamePatchesAgentByDirByBaseErrors[keyof GetV0CityByCityNamePatchesAgentByDirByBaseErrors]; + +export type GetV0CityByCityNamePatchesAgentByDirByBaseResponses = { + /** + * OK + */ + 200: AgentPatch; +}; + +export type GetV0CityByCityNamePatchesAgentByDirByBaseResponse = GetV0CityByCityNamePatchesAgentByDirByBaseResponses[keyof GetV0CityByCityNamePatchesAgentByDirByBaseResponses]; + +export type GetV0CityByCityNamePatchesAgentsData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/agents'; +}; + +export type GetV0CityByCityNamePatchesAgentsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNamePatchesAgentsError = GetV0CityByCityNamePatchesAgentsErrors[keyof GetV0CityByCityNamePatchesAgentsErrors]; + +export type GetV0CityByCityNamePatchesAgentsResponses = { + /** + * OK + */ + 200: ListBodyAgentPatch; +}; + +export type GetV0CityByCityNamePatchesAgentsResponse = GetV0CityByCityNamePatchesAgentsResponses[keyof GetV0CityByCityNamePatchesAgentsResponses]; + +export type PutV0CityByCityNamePatchesAgentsData = { + body: AgentPatchSetInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/agents'; +}; + +export type PutV0CityByCityNamePatchesAgentsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PutV0CityByCityNamePatchesAgentsError = PutV0CityByCityNamePatchesAgentsErrors[keyof PutV0CityByCityNamePatchesAgentsErrors]; + +export type PutV0CityByCityNamePatchesAgentsResponses = { + /** + * OK + */ + 200: PatchOkResponseBody; +}; + +export type PutV0CityByCityNamePatchesAgentsResponse = PutV0CityByCityNamePatchesAgentsResponses[keyof PutV0CityByCityNamePatchesAgentsResponses]; + +export type DeleteV0CityByCityNamePatchesProviderByNameData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Provider patch name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/provider/{name}'; +}; + +export type DeleteV0CityByCityNamePatchesProviderByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNamePatchesProviderByNameError = DeleteV0CityByCityNamePatchesProviderByNameErrors[keyof DeleteV0CityByCityNamePatchesProviderByNameErrors]; + +export type DeleteV0CityByCityNamePatchesProviderByNameResponses = { + /** + * OK + */ + 200: PatchDeletedResponseBody; +}; + +export type DeleteV0CityByCityNamePatchesProviderByNameResponse = DeleteV0CityByCityNamePatchesProviderByNameResponses[keyof DeleteV0CityByCityNamePatchesProviderByNameResponses]; + +export type GetV0CityByCityNamePatchesProviderByNameData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Provider patch name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/provider/{name}'; +}; + +export type GetV0CityByCityNamePatchesProviderByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNamePatchesProviderByNameError = GetV0CityByCityNamePatchesProviderByNameErrors[keyof GetV0CityByCityNamePatchesProviderByNameErrors]; + +export type GetV0CityByCityNamePatchesProviderByNameResponses = { + /** + * OK + */ + 200: ProviderPatch; +}; + +export type GetV0CityByCityNamePatchesProviderByNameResponse = GetV0CityByCityNamePatchesProviderByNameResponses[keyof GetV0CityByCityNamePatchesProviderByNameResponses]; + +export type GetV0CityByCityNamePatchesProvidersData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/providers'; +}; + +export type GetV0CityByCityNamePatchesProvidersErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNamePatchesProvidersError = GetV0CityByCityNamePatchesProvidersErrors[keyof GetV0CityByCityNamePatchesProvidersErrors]; + +export type GetV0CityByCityNamePatchesProvidersResponses = { + /** + * OK + */ + 200: ListBodyProviderPatch; +}; + +export type GetV0CityByCityNamePatchesProvidersResponse = GetV0CityByCityNamePatchesProvidersResponses[keyof GetV0CityByCityNamePatchesProvidersResponses]; + +export type PutV0CityByCityNamePatchesProvidersData = { + body: ProviderPatchSetInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/providers'; +}; + +export type PutV0CityByCityNamePatchesProvidersErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PutV0CityByCityNamePatchesProvidersError = PutV0CityByCityNamePatchesProvidersErrors[keyof PutV0CityByCityNamePatchesProvidersErrors]; + +export type PutV0CityByCityNamePatchesProvidersResponses = { + /** + * OK + */ + 200: PatchOkResponseBody; +}; + +export type PutV0CityByCityNamePatchesProvidersResponse = PutV0CityByCityNamePatchesProvidersResponses[keyof PutV0CityByCityNamePatchesProvidersResponses]; + +export type DeleteV0CityByCityNamePatchesRigByNameData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Rig patch name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/rig/{name}'; +}; + +export type DeleteV0CityByCityNamePatchesRigByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNamePatchesRigByNameError = DeleteV0CityByCityNamePatchesRigByNameErrors[keyof DeleteV0CityByCityNamePatchesRigByNameErrors]; + +export type DeleteV0CityByCityNamePatchesRigByNameResponses = { + /** + * OK + */ + 200: PatchDeletedResponseBody; +}; + +export type DeleteV0CityByCityNamePatchesRigByNameResponse = DeleteV0CityByCityNamePatchesRigByNameResponses[keyof DeleteV0CityByCityNamePatchesRigByNameResponses]; + +export type GetV0CityByCityNamePatchesRigByNameData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Rig patch name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/rig/{name}'; +}; + +export type GetV0CityByCityNamePatchesRigByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNamePatchesRigByNameError = GetV0CityByCityNamePatchesRigByNameErrors[keyof GetV0CityByCityNamePatchesRigByNameErrors]; + +export type GetV0CityByCityNamePatchesRigByNameResponses = { + /** + * OK + */ + 200: RigPatch; +}; + +export type GetV0CityByCityNamePatchesRigByNameResponse = GetV0CityByCityNamePatchesRigByNameResponses[keyof GetV0CityByCityNamePatchesRigByNameResponses]; + +export type GetV0CityByCityNamePatchesRigsData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/rigs'; +}; + +export type GetV0CityByCityNamePatchesRigsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNamePatchesRigsError = GetV0CityByCityNamePatchesRigsErrors[keyof GetV0CityByCityNamePatchesRigsErrors]; + +export type GetV0CityByCityNamePatchesRigsResponses = { + /** + * OK + */ + 200: ListBodyRigPatch; +}; + +export type GetV0CityByCityNamePatchesRigsResponse = GetV0CityByCityNamePatchesRigsResponses[keyof GetV0CityByCityNamePatchesRigsResponses]; + +export type PutV0CityByCityNamePatchesRigsData = { + body: RigPatchSetInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/patches/rigs'; +}; + +export type PutV0CityByCityNamePatchesRigsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PutV0CityByCityNamePatchesRigsError = PutV0CityByCityNamePatchesRigsErrors[keyof PutV0CityByCityNamePatchesRigsErrors]; + +export type PutV0CityByCityNamePatchesRigsResponses = { + /** + * OK + */ + 200: PatchOkResponseBody; +}; + +export type PutV0CityByCityNamePatchesRigsResponse = PutV0CityByCityNamePatchesRigsResponses[keyof PutV0CityByCityNamePatchesRigsResponses]; + +export type GetV0CityByCityNameProviderReadinessData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Comma-separated provider names to check (default: claude,codex,gemini). + */ + providers?: string; + /** + * Force fresh probe, bypassing cache. + */ + fresh?: boolean; + }; + url: '/v0/city/{cityName}/provider-readiness'; +}; + +export type GetV0CityByCityNameProviderReadinessErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameProviderReadinessError = GetV0CityByCityNameProviderReadinessErrors[keyof GetV0CityByCityNameProviderReadinessErrors]; + +export type GetV0CityByCityNameProviderReadinessResponses = { + /** + * OK + */ + 200: ProviderReadinessResponse; +}; + +export type GetV0CityByCityNameProviderReadinessResponse = GetV0CityByCityNameProviderReadinessResponses[keyof GetV0CityByCityNameProviderReadinessResponses]; + +export type DeleteV0CityByCityNameProviderByNameData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Provider name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/provider/{name}'; +}; + +export type DeleteV0CityByCityNameProviderByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNameProviderByNameError = DeleteV0CityByCityNameProviderByNameErrors[keyof DeleteV0CityByCityNameProviderByNameErrors]; + +export type DeleteV0CityByCityNameProviderByNameResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type DeleteV0CityByCityNameProviderByNameResponse = DeleteV0CityByCityNameProviderByNameResponses[keyof DeleteV0CityByCityNameProviderByNameResponses]; + +export type GetV0CityByCityNameProviderByNameData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Provider name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/provider/{name}'; +}; + +export type GetV0CityByCityNameProviderByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameProviderByNameError = GetV0CityByCityNameProviderByNameErrors[keyof GetV0CityByCityNameProviderByNameErrors]; + +export type GetV0CityByCityNameProviderByNameResponses = { + /** + * OK + */ + 200: ProviderResponse; +}; + +export type GetV0CityByCityNameProviderByNameResponse = GetV0CityByCityNameProviderByNameResponses[keyof GetV0CityByCityNameProviderByNameResponses]; + +export type PatchV0CityByCityNameProviderByNameData = { + body: ProviderUpdateInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Provider name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/provider/{name}'; +}; + +export type PatchV0CityByCityNameProviderByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PatchV0CityByCityNameProviderByNameError = PatchV0CityByCityNameProviderByNameErrors[keyof PatchV0CityByCityNameProviderByNameErrors]; + +export type PatchV0CityByCityNameProviderByNameResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PatchV0CityByCityNameProviderByNameResponse = PatchV0CityByCityNameProviderByNameResponses[keyof PatchV0CityByCityNameProviderByNameResponses]; + +export type GetV0CityByCityNameProvidersData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/providers'; +}; + +export type GetV0CityByCityNameProvidersErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameProvidersError = GetV0CityByCityNameProvidersErrors[keyof GetV0CityByCityNameProvidersErrors]; + +export type GetV0CityByCityNameProvidersResponses = { + /** + * OK + */ + 200: ListBodyProviderResponse; +}; + +export type GetV0CityByCityNameProvidersResponse = GetV0CityByCityNameProvidersResponses[keyof GetV0CityByCityNameProvidersResponses]; + +export type CreateProviderData = { + body: ProviderCreateInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/providers'; +}; + +export type CreateProviderErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type CreateProviderError = CreateProviderErrors[keyof CreateProviderErrors]; + +export type CreateProviderResponses = { + /** + * Created + */ + 201: ProviderCreatedOutputBody; +}; + +export type CreateProviderResponse = CreateProviderResponses[keyof CreateProviderResponses]; + +export type GetV0CityByCityNameProvidersPublicData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/providers/public'; +}; + +export type GetV0CityByCityNameProvidersPublicErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameProvidersPublicError = GetV0CityByCityNameProvidersPublicErrors[keyof GetV0CityByCityNameProvidersPublicErrors]; + +export type GetV0CityByCityNameProvidersPublicResponses = { + /** + * OK + */ + 200: ProviderPublicListBody; +}; + +export type GetV0CityByCityNameProvidersPublicResponse = GetV0CityByCityNameProvidersPublicResponses[keyof GetV0CityByCityNameProvidersPublicResponses]; + +export type GetV0CityByCityNameReadinessData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Comma-separated readiness items to check (default: claude,codex,gemini,github_cli). + */ + items?: string; + /** + * Force fresh probe, bypassing cache. + */ + fresh?: boolean; + }; + url: '/v0/city/{cityName}/readiness'; +}; + +export type GetV0CityByCityNameReadinessErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameReadinessError = GetV0CityByCityNameReadinessErrors[keyof GetV0CityByCityNameReadinessErrors]; + +export type GetV0CityByCityNameReadinessResponses = { + /** + * OK + */ + 200: ReadinessResponse; +}; + +export type GetV0CityByCityNameReadinessResponse = GetV0CityByCityNameReadinessResponses[keyof GetV0CityByCityNameReadinessResponses]; + +export type DeleteV0CityByCityNameRigByNameData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Rig name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/rig/{name}'; +}; + +export type DeleteV0CityByCityNameRigByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNameRigByNameError = DeleteV0CityByCityNameRigByNameErrors[keyof DeleteV0CityByCityNameRigByNameErrors]; + +export type DeleteV0CityByCityNameRigByNameResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type DeleteV0CityByCityNameRigByNameResponse = DeleteV0CityByCityNameRigByNameResponses[keyof DeleteV0CityByCityNameRigByNameResponses]; + +export type GetV0CityByCityNameRigByNameData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Rig name. + */ + name: string; + }; + query?: { + /** + * Include git status. + */ + git?: boolean; + }; + url: '/v0/city/{cityName}/rig/{name}'; +}; + +export type GetV0CityByCityNameRigByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameRigByNameError = GetV0CityByCityNameRigByNameErrors[keyof GetV0CityByCityNameRigByNameErrors]; + +export type GetV0CityByCityNameRigByNameResponses = { + /** + * OK + */ + 200: RigResponse; +}; + +export type GetV0CityByCityNameRigByNameResponse = GetV0CityByCityNameRigByNameResponses[keyof GetV0CityByCityNameRigByNameResponses]; + +export type PatchV0CityByCityNameRigByNameData = { + body: RigUpdateInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Rig name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/rig/{name}'; +}; + +export type PatchV0CityByCityNameRigByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PatchV0CityByCityNameRigByNameError = PatchV0CityByCityNameRigByNameErrors[keyof PatchV0CityByCityNameRigByNameErrors]; + +export type PatchV0CityByCityNameRigByNameResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PatchV0CityByCityNameRigByNameResponse = PatchV0CityByCityNameRigByNameResponses[keyof PatchV0CityByCityNameRigByNameResponses]; + +export type PostV0CityByCityNameRigByNameByActionData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Rig name. + */ + name: string; + /** + * Action to perform (suspend, resume, restart). + */ + action: string; + }; + query?: never; + url: '/v0/city/{cityName}/rig/{name}/{action}'; +}; + +export type PostV0CityByCityNameRigByNameByActionErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameRigByNameByActionError = PostV0CityByCityNameRigByNameByActionErrors[keyof PostV0CityByCityNameRigByNameByActionErrors]; + +export type PostV0CityByCityNameRigByNameByActionResponses = { + /** + * OK + */ + 200: RigActionBody; +}; + +export type PostV0CityByCityNameRigByNameByActionResponse = PostV0CityByCityNameRigByNameByActionResponses[keyof PostV0CityByCityNameRigByNameByActionResponses]; + +export type GetV0CityByCityNameRigsData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Event sequence number; when provided, blocks until a newer event arrives. + */ + index?: string; + /** + * How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + */ + wait?: string; + /** + * Include git status. + */ + git?: boolean; + }; + url: '/v0/city/{cityName}/rigs'; +}; + +export type GetV0CityByCityNameRigsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameRigsError = GetV0CityByCityNameRigsErrors[keyof GetV0CityByCityNameRigsErrors]; + +export type GetV0CityByCityNameRigsResponses = { + /** + * OK + */ + 200: ListBodyRigResponse; +}; + +export type GetV0CityByCityNameRigsResponse = GetV0CityByCityNameRigsResponses[keyof GetV0CityByCityNameRigsResponses]; + +export type CreateRigData = { + body: RigCreateInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/rigs'; +}; + +export type CreateRigErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type CreateRigError = CreateRigErrors[keyof CreateRigErrors]; + +export type CreateRigResponses = { + /** + * Created + */ + 201: RigCreatedOutputBody; +}; + +export type CreateRigResponse = CreateRigResponses[keyof CreateRigResponses]; + +export type GetV0CityByCityNameServiceByNameData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Service name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/service/{name}'; +}; + +export type GetV0CityByCityNameServiceByNameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameServiceByNameError = GetV0CityByCityNameServiceByNameErrors[keyof GetV0CityByCityNameServiceByNameErrors]; + +export type GetV0CityByCityNameServiceByNameResponses = { + /** + * OK + */ + 200: Status; +}; + +export type GetV0CityByCityNameServiceByNameResponse = GetV0CityByCityNameServiceByNameResponses[keyof GetV0CityByCityNameServiceByNameResponses]; + +export type PostV0CityByCityNameServiceByNameRestartData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Service name. + */ + name: string; + }; + query?: never; + url: '/v0/city/{cityName}/service/{name}/restart'; +}; + +export type PostV0CityByCityNameServiceByNameRestartErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameServiceByNameRestartError = PostV0CityByCityNameServiceByNameRestartErrors[keyof PostV0CityByCityNameServiceByNameRestartErrors]; + +export type PostV0CityByCityNameServiceByNameRestartResponses = { + /** + * OK + */ + 200: ServiceRestartOutputBody; +}; + +export type PostV0CityByCityNameServiceByNameRestartResponse = PostV0CityByCityNameServiceByNameRestartResponses[keyof PostV0CityByCityNameServiceByNameRestartResponses]; + +export type GetV0CityByCityNameServicesData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/services'; +}; + +export type GetV0CityByCityNameServicesErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameServicesError = GetV0CityByCityNameServicesErrors[keyof GetV0CityByCityNameServicesErrors]; + +export type GetV0CityByCityNameServicesResponses = { + /** + * OK + */ + 200: ListBodyStatus; +}; + +export type GetV0CityByCityNameServicesResponse = GetV0CityByCityNameServicesResponses[keyof GetV0CityByCityNameServicesResponses]; + +export type GetV0CityByCityNameSessionByIdData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: { + /** + * Include last output preview. + */ + peek?: boolean; + }; + url: '/v0/city/{cityName}/session/{id}'; +}; + +export type GetV0CityByCityNameSessionByIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameSessionByIdError = GetV0CityByCityNameSessionByIdErrors[keyof GetV0CityByCityNameSessionByIdErrors]; + +export type GetV0CityByCityNameSessionByIdResponses = { + /** + * OK + */ + 200: SessionResponse; +}; + +export type GetV0CityByCityNameSessionByIdResponse = GetV0CityByCityNameSessionByIdResponses[keyof GetV0CityByCityNameSessionByIdResponses]; + +export type PatchV0CityByCityNameSessionByIdData = { + body: SessionPatchBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}'; +}; + +export type PatchV0CityByCityNameSessionByIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PatchV0CityByCityNameSessionByIdError = PatchV0CityByCityNameSessionByIdErrors[keyof PatchV0CityByCityNameSessionByIdErrors]; + +export type PatchV0CityByCityNameSessionByIdResponses = { + /** + * OK + */ + 200: SessionResponse; +}; + +export type PatchV0CityByCityNameSessionByIdResponse = PatchV0CityByCityNameSessionByIdResponses[keyof PatchV0CityByCityNameSessionByIdResponses]; + +export type GetV0CityByCityNameSessionByIdAgentsData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}/agents'; +}; + +export type GetV0CityByCityNameSessionByIdAgentsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameSessionByIdAgentsError = GetV0CityByCityNameSessionByIdAgentsErrors[keyof GetV0CityByCityNameSessionByIdAgentsErrors]; + +export type GetV0CityByCityNameSessionByIdAgentsResponses = { + /** + * OK + */ + 200: SessionAgentListResponse; +}; + +export type GetV0CityByCityNameSessionByIdAgentsResponse = GetV0CityByCityNameSessionByIdAgentsResponses[keyof GetV0CityByCityNameSessionByIdAgentsResponses]; + +export type GetV0CityByCityNameSessionByIdAgentsByAgentIdData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + /** + * Subagent ID within the session. + */ + agentId: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}/agents/{agentId}'; +}; + +export type GetV0CityByCityNameSessionByIdAgentsByAgentIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameSessionByIdAgentsByAgentIdError = GetV0CityByCityNameSessionByIdAgentsByAgentIdErrors[keyof GetV0CityByCityNameSessionByIdAgentsByAgentIdErrors]; + +export type GetV0CityByCityNameSessionByIdAgentsByAgentIdResponses = { + /** + * OK + */ + 200: SessionAgentGetResponse; +}; + +export type GetV0CityByCityNameSessionByIdAgentsByAgentIdResponse = GetV0CityByCityNameSessionByIdAgentsByAgentIdResponses[keyof GetV0CityByCityNameSessionByIdAgentsByAgentIdResponses]; + +export type PostV0CityByCityNameSessionByIdCloseData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: { + /** + * Permanently delete bead after closing. + */ + delete?: boolean; + }; + url: '/v0/city/{cityName}/session/{id}/close'; +}; + +export type PostV0CityByCityNameSessionByIdCloseErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameSessionByIdCloseError = PostV0CityByCityNameSessionByIdCloseErrors[keyof PostV0CityByCityNameSessionByIdCloseErrors]; + +export type PostV0CityByCityNameSessionByIdCloseResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameSessionByIdCloseResponse = PostV0CityByCityNameSessionByIdCloseResponses[keyof PostV0CityByCityNameSessionByIdCloseResponses]; + +export type PostV0CityByCityNameSessionByIdKillData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}/kill'; +}; + +export type PostV0CityByCityNameSessionByIdKillErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameSessionByIdKillError = PostV0CityByCityNameSessionByIdKillErrors[keyof PostV0CityByCityNameSessionByIdKillErrors]; + +export type PostV0CityByCityNameSessionByIdKillResponses = { + /** + * OK + */ + 200: OkWithIdResponseBody; +}; + +export type PostV0CityByCityNameSessionByIdKillResponse = PostV0CityByCityNameSessionByIdKillResponses[keyof PostV0CityByCityNameSessionByIdKillResponses]; + +export type SendSessionMessageData = { + body: SessionMessageInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}/messages'; +}; + +export type SendSessionMessageErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type SendSessionMessageError = SendSessionMessageErrors[keyof SendSessionMessageErrors]; + +export type SendSessionMessageResponses = { + /** + * Accepted + */ + 202: SessionMessageOutputBody; +}; + +export type SendSessionMessageResponse = SendSessionMessageResponses[keyof SendSessionMessageResponses]; + +export type GetV0CityByCityNameSessionByIdPendingData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}/pending'; +}; + +export type GetV0CityByCityNameSessionByIdPendingErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameSessionByIdPendingError = GetV0CityByCityNameSessionByIdPendingErrors[keyof GetV0CityByCityNameSessionByIdPendingErrors]; + +export type GetV0CityByCityNameSessionByIdPendingResponses = { + /** + * OK + */ + 200: SessionPendingResponse; +}; + +export type GetV0CityByCityNameSessionByIdPendingResponse = GetV0CityByCityNameSessionByIdPendingResponses[keyof GetV0CityByCityNameSessionByIdPendingResponses]; + +export type PostV0CityByCityNameSessionByIdRenameData = { + body: SessionRenameInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}/rename'; +}; + +export type PostV0CityByCityNameSessionByIdRenameErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameSessionByIdRenameError = PostV0CityByCityNameSessionByIdRenameErrors[keyof PostV0CityByCityNameSessionByIdRenameErrors]; + +export type PostV0CityByCityNameSessionByIdRenameResponses = { + /** + * OK + */ + 200: SessionResponse; +}; + +export type PostV0CityByCityNameSessionByIdRenameResponse = PostV0CityByCityNameSessionByIdRenameResponses[keyof PostV0CityByCityNameSessionByIdRenameResponses]; + +export type RespondSessionData = { + body: SessionRespondInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}/respond'; +}; + +export type RespondSessionErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type RespondSessionError = RespondSessionErrors[keyof RespondSessionErrors]; + +export type RespondSessionResponses = { + /** + * Accepted + */ + 202: SessionRespondOutputBody; +}; + +export type RespondSessionResponse = RespondSessionResponses[keyof RespondSessionResponses]; + +export type PostV0CityByCityNameSessionByIdStopData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}/stop'; +}; + +export type PostV0CityByCityNameSessionByIdStopErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameSessionByIdStopError = PostV0CityByCityNameSessionByIdStopErrors[keyof PostV0CityByCityNameSessionByIdStopErrors]; + +export type PostV0CityByCityNameSessionByIdStopResponses = { + /** + * OK + */ + 200: OkWithIdResponseBody; +}; + +export type PostV0CityByCityNameSessionByIdStopResponse = PostV0CityByCityNameSessionByIdStopResponses[keyof PostV0CityByCityNameSessionByIdStopResponses]; + +export type StreamSessionData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: { + /** + * Transcript format: conversation (default) or raw. + */ + format?: string; + }; + url: '/v0/city/{cityName}/session/{id}/stream'; +}; + +export type StreamSessionErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type StreamSessionError = StreamSessionErrors[keyof StreamSessionErrors]; + +export type StreamSessionResponses = { + /** + * Server Sent Events + * + * Each oneOf object represents one possible SSE message. + */ + 200: Array<{ + data: SessionActivityEvent; + /** + * The event name. + */ + event: 'activity'; + /** + * The event ID. + */ + id?: number; + /** + * The retry time in milliseconds. + */ + retry?: number; + } | { + data: HeartbeatEvent; + /** + * The event name. + */ + event: 'heartbeat'; + /** + * The event ID. + */ + id?: number; + /** + * The retry time in milliseconds. + */ + retry?: number; + } | { + data: SessionStreamRawMessageEvent; + /** + * The event name. + */ + event?: 'message'; + /** + * The event ID. + */ + id?: number; + /** + * The retry time in milliseconds. + */ + retry?: number; + } | { + data: PendingInteraction; + /** + * The event name. + */ + event: 'pending'; + /** + * The event ID. + */ + id?: number; + /** + * The retry time in milliseconds. + */ + retry?: number; + } | { + data: SessionStreamMessageEvent; + /** + * The event name. + */ + event: 'turn'; + /** + * The event ID. + */ + id?: number; + /** + * The retry time in milliseconds. + */ + retry?: number; + }>; +}; + +export type StreamSessionResponse = StreamSessionResponses[keyof StreamSessionResponses]; + +export type SubmitSessionData = { + body: SessionSubmitInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}/submit'; +}; + +export type SubmitSessionErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type SubmitSessionError = SubmitSessionErrors[keyof SubmitSessionErrors]; + +export type SubmitSessionResponses = { + /** + * Accepted + */ + 202: SessionSubmitOutputBody; +}; + +export type SubmitSessionResponse = SubmitSessionResponses[keyof SubmitSessionResponses]; + +export type PostV0CityByCityNameSessionByIdSuspendData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}/suspend'; +}; + +export type PostV0CityByCityNameSessionByIdSuspendErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameSessionByIdSuspendError = PostV0CityByCityNameSessionByIdSuspendErrors[keyof PostV0CityByCityNameSessionByIdSuspendErrors]; + +export type PostV0CityByCityNameSessionByIdSuspendResponses = { + /** + * OK + */ + 200: OkResponseBody; +}; + +export type PostV0CityByCityNameSessionByIdSuspendResponse = PostV0CityByCityNameSessionByIdSuspendResponses[keyof PostV0CityByCityNameSessionByIdSuspendResponses]; + +export type GetV0CityByCityNameSessionByIdTranscriptData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: { + /** + * Number of recent compaction segments to return. This API parameter keeps compaction-segment semantics even though gc session logs --tail counts displayed transcript entries. Omit for the endpoint default (usually 1); 0 returns all segments; N>0 returns the last N. + */ + tail?: string; + /** + * Transcript format: conversation (default) or raw. + */ + format?: string; + /** + * Pagination cursor: return entries before this UUID. + */ + before?: string; + }; + url: '/v0/city/{cityName}/session/{id}/transcript'; +}; + +export type GetV0CityByCityNameSessionByIdTranscriptErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameSessionByIdTranscriptError = GetV0CityByCityNameSessionByIdTranscriptErrors[keyof GetV0CityByCityNameSessionByIdTranscriptErrors]; + +export type GetV0CityByCityNameSessionByIdTranscriptResponses = { + /** + * OK + */ + 200: SessionTranscriptGetResponse; +}; + +export type GetV0CityByCityNameSessionByIdTranscriptResponse = GetV0CityByCityNameSessionByIdTranscriptResponses[keyof GetV0CityByCityNameSessionByIdTranscriptResponses]; + +export type PostV0CityByCityNameSessionByIdWakeData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}/wake'; +}; + +export type PostV0CityByCityNameSessionByIdWakeErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameSessionByIdWakeError = PostV0CityByCityNameSessionByIdWakeErrors[keyof PostV0CityByCityNameSessionByIdWakeErrors]; + +export type PostV0CityByCityNameSessionByIdWakeResponses = { + /** + * OK + */ + 200: OkWithIdResponseBody; +}; + +export type PostV0CityByCityNameSessionByIdWakeResponse = PostV0CityByCityNameSessionByIdWakeResponses[keyof PostV0CityByCityNameSessionByIdWakeResponses]; + +export type GetV0CityByCityNameSessionsData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Pagination cursor from a previous response's next_cursor field. + */ + cursor?: string; + /** + * Maximum number of results to return. 0 = server default. + */ + limit?: number; + /** + * Filter by session state (e.g. active, closed). + */ + state?: string; + /** + * Filter by session template (agent qualified name). + */ + template?: string; + /** + * Include last output preview. + */ + peek?: boolean; + }; + url: '/v0/city/{cityName}/sessions'; +}; + +export type GetV0CityByCityNameSessionsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameSessionsError = GetV0CityByCityNameSessionsErrors[keyof GetV0CityByCityNameSessionsErrors]; + +export type GetV0CityByCityNameSessionsResponses = { + /** + * OK + */ + 200: ListBodySessionResponse; +}; + +export type GetV0CityByCityNameSessionsResponse = GetV0CityByCityNameSessionsResponses[keyof GetV0CityByCityNameSessionsResponses]; + +export type CreateSessionData = { + body: SessionCreateBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/sessions'; +}; + +export type CreateSessionErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type CreateSessionError = CreateSessionErrors[keyof CreateSessionErrors]; + +export type CreateSessionResponses = { + /** + * Accepted + */ + 202: SessionResponse; +}; + +export type CreateSessionResponse = CreateSessionResponses[keyof CreateSessionResponses]; + +export type PostV0CityByCityNameSlingData = { + body: SlingInputBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/sling'; +}; + +export type PostV0CityByCityNameSlingErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameSlingError = PostV0CityByCityNameSlingErrors[keyof PostV0CityByCityNameSlingErrors]; + +export type PostV0CityByCityNameSlingResponses = { + /** + * OK + */ + 200: SlingResponse; +}; + +export type PostV0CityByCityNameSlingResponse = PostV0CityByCityNameSlingResponses[keyof PostV0CityByCityNameSlingResponses]; + +export type GetV0CityByCityNameStatusData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + }; + query?: { + /** + * Event sequence number; when provided, blocks until a newer event arrives. + */ + index?: string; + /** + * How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + */ + wait?: string; + }; + url: '/v0/city/{cityName}/status'; +}; + +export type GetV0CityByCityNameStatusErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameStatusError = GetV0CityByCityNameStatusErrors[keyof GetV0CityByCityNameStatusErrors]; + +export type GetV0CityByCityNameStatusResponses = { + /** + * OK + */ + 200: StatusBody; +}; + +export type GetV0CityByCityNameStatusResponse = GetV0CityByCityNameStatusResponses[keyof GetV0CityByCityNameStatusResponses]; + +export type PostV0CityByCityNameUnregisterData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * Supervisor-registered city name. + */ + cityName: string; + }; + query?: never; + url: '/v0/city/{cityName}/unregister'; +}; + +export type PostV0CityByCityNameUnregisterErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameUnregisterError = PostV0CityByCityNameUnregisterErrors[keyof PostV0CityByCityNameUnregisterErrors]; + +export type PostV0CityByCityNameUnregisterResponses = { + /** + * Accepted + */ + 202: CityUnregisterResponse; +}; + +export type PostV0CityByCityNameUnregisterResponse = PostV0CityByCityNameUnregisterResponses[keyof PostV0CityByCityNameUnregisterResponses]; + +export type DeleteV0CityByCityNameWorkflowByWorkflowIdData = { + body?: never; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Workflow (convoy) ID. + */ + workflow_id: string; + }; + query?: { + /** + * Scope kind (city or rig). + */ + scope_kind?: string; + /** + * Scope reference. + */ + scope_ref?: string; + /** + * Permanently delete beads from store. + */ + delete?: boolean; + }; + url: '/v0/city/{cityName}/workflow/{workflow_id}'; +}; + +export type DeleteV0CityByCityNameWorkflowByWorkflowIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type DeleteV0CityByCityNameWorkflowByWorkflowIdError = DeleteV0CityByCityNameWorkflowByWorkflowIdErrors[keyof DeleteV0CityByCityNameWorkflowByWorkflowIdErrors]; + +export type DeleteV0CityByCityNameWorkflowByWorkflowIdResponses = { + /** + * OK + */ + 200: WorkflowDeleteResponse; +}; + +export type DeleteV0CityByCityNameWorkflowByWorkflowIdResponse = DeleteV0CityByCityNameWorkflowByWorkflowIdResponses[keyof DeleteV0CityByCityNameWorkflowByWorkflowIdResponses]; + +export type GetV0CityByCityNameWorkflowByWorkflowIdData = { + body?: never; + path: { + /** + * City name. + */ + cityName: string; + /** + * Workflow (convoy) ID. + */ + workflow_id: string; + }; + query?: { + /** + * Scope kind (city or rig). + */ + scope_kind?: string; + /** + * Scope reference. + */ + scope_ref?: string; + }; + url: '/v0/city/{cityName}/workflow/{workflow_id}'; +}; + +export type GetV0CityByCityNameWorkflowByWorkflowIdErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0CityByCityNameWorkflowByWorkflowIdError = GetV0CityByCityNameWorkflowByWorkflowIdErrors[keyof GetV0CityByCityNameWorkflowByWorkflowIdErrors]; + +export type GetV0CityByCityNameWorkflowByWorkflowIdResponses = { + /** + * OK + */ + 200: WorkflowSnapshotResponse; +}; + +export type GetV0CityByCityNameWorkflowByWorkflowIdResponse = GetV0CityByCityNameWorkflowByWorkflowIdResponses[keyof GetV0CityByCityNameWorkflowByWorkflowIdResponses]; + +export type GetV0EventsData = { + body?: never; + path?: never; + query?: { + /** + * Filter by event type. + */ + type?: string; + /** + * Filter by actor. + */ + actor?: string; + /** + * Filter to events within the last Go duration (e.g. "5m"). + */ + since?: string; + /** + * Maximum number of trailing events to return. 0 = no limit. Used by 'gc events --seq' to compute the head cursor cheaply. + */ + limit?: number; + }; + url: '/v0/events'; +}; + +export type GetV0EventsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0EventsError = GetV0EventsErrors[keyof GetV0EventsErrors]; + +export type GetV0EventsResponses = { + /** + * OK + */ + 200: SupervisorEventListOutputBody; +}; + +export type GetV0EventsResponse = GetV0EventsResponses[keyof GetV0EventsResponses]; + +export type StreamSupervisorEventsData = { + body?: never; + headers?: { + /** + * Reconnect cursor (composite per-city cursor). + */ + 'Last-Event-ID'?: string; + }; + path?: never; + query?: { + /** + * Alternative to Last-Event-ID for browsers that can't set custom headers. + */ + after_cursor?: string; + }; + url: '/v0/events/stream'; +}; + +export type StreamSupervisorEventsErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type StreamSupervisorEventsError = StreamSupervisorEventsErrors[keyof StreamSupervisorEventsErrors]; + +export type StreamSupervisorEventsResponses = { + /** + * Server Sent Events + * + * Each oneOf object represents one possible SSE message. + */ + 200: Array<{ + data: HeartbeatEvent; + /** + * The event name. + */ + event: 'heartbeat'; + /** + * The event ID (composite cursor). + */ + id?: string; + /** + * The retry time in milliseconds. + */ + retry?: number; + } | { + data: TypedTaggedEventStreamEnvelope; + /** + * The event name. + */ + event: 'tagged_event'; + /** + * The event ID (composite cursor). + */ + id?: string; + /** + * The retry time in milliseconds. + */ + retry?: number; + }>; +}; + +export type StreamSupervisorEventsResponse = StreamSupervisorEventsResponses[keyof StreamSupervisorEventsResponses]; + +export type GetV0ProviderReadinessData = { + body?: never; + path?: never; + query?: { + /** + * Comma-separated list of providers to probe. + */ + providers?: string; + /** + * Force fresh probe, bypassing cache. + */ + fresh?: boolean; + }; + url: '/v0/provider-readiness'; +}; + +export type GetV0ProviderReadinessErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0ProviderReadinessError = GetV0ProviderReadinessErrors[keyof GetV0ProviderReadinessErrors]; + +export type GetV0ProviderReadinessResponses = { + /** + * OK + */ + 200: ProviderReadinessResponse; +}; + +export type GetV0ProviderReadinessResponse = GetV0ProviderReadinessResponses[keyof GetV0ProviderReadinessResponses]; + +export type GetV0ReadinessData = { + body?: never; + path?: never; + query?: { + /** + * Comma-separated list of readiness items to check. + */ + items?: string; + /** + * Force fresh probe, bypassing cache. + */ + fresh?: boolean; + }; + url: '/v0/readiness'; +}; + +export type GetV0ReadinessErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type GetV0ReadinessError = GetV0ReadinessErrors[keyof GetV0ReadinessErrors]; + +export type GetV0ReadinessResponses = { + /** + * OK + */ + 200: ReadinessResponse; +}; + +export type GetV0ReadinessResponse = GetV0ReadinessResponses[keyof GetV0ReadinessResponses]; diff --git a/cmd/gc/dashboard/web/src/panels/admin.ts b/cmd/gc/dashboard/web/src/panels/admin.ts index 600716590d..4fb76943c1 100644 --- a/cmd/gc/dashboard/web/src/panels/admin.ts +++ b/cmd/gc/dashboard/web/src/panels/admin.ts @@ -1,5 +1,5 @@ import type { BeadRecord, RigRecord, ServiceStatusRecord } from "../api"; -import { api, cityScope } from "../api"; +import { api, cityScope, mutationHeaders } from "../api"; import { promptActionDialog, promptConfirmDialog } from "../modals"; import { byId, clear, el } from "../util/dom"; import { formatAgentAddress, formatTimestamp, statusBadgeClass, truncate } from "../util/legacy"; @@ -309,7 +309,7 @@ export async function openAssignModal(beadID = ""): Promise { }); if (!selection) return; const res = await api.POST("/v0/city/{cityName}/sling", { - params: { path: { cityName: city } }, + params: { path: { cityName: city }, header: mutationHeaders }, body: { bead: selection.beadID, target: selection.target, rig: selection.rig || undefined }, }); if (res.error) { @@ -339,7 +339,7 @@ async function clearAllAssigned(): Promise { if (!confirmed) return; await Promise.all(items.map((bead) => api.POST("/v0/city/{cityName}/bead/{id}/assign", { - params: { path: { cityName: city, id: bead.id ?? "" } }, + params: { path: { cityName: city, id: bead.id ?? "" }, header: mutationHeaders }, body: { assignee: "" }, }), )); @@ -351,7 +351,7 @@ async function unassignBead(beadID: string): Promise { const city = cityScope(); if (!city) return; const res = await api.POST("/v0/city/{cityName}/bead/{id}/assign", { - params: { path: { cityName: city, id: beadID } }, + params: { path: { cityName: city, id: beadID }, header: mutationHeaders }, body: { assignee: "" }, }); if (res.error) { @@ -366,7 +366,7 @@ async function restartService(service: string): Promise { const city = cityScope(); if (!city) return; const res = await api.POST("/v0/city/{cityName}/service/{name}/restart", { - params: { path: { cityName: city, name: service } }, + params: { path: { cityName: city, name: service }, header: mutationHeaders }, }); if (res.error) { showToast("error", "Service failed", res.error.detail ?? "Could not restart service"); @@ -380,7 +380,7 @@ async function rigAction(rig: string, action: string): Promise { const city = cityScope(); if (!city) return; const res = await api.POST("/v0/city/{cityName}/rig/{name}/{action}", { - params: { path: { cityName: city, name: rig, action } }, + params: { path: { cityName: city, name: rig, action }, header: mutationHeaders }, }); if (res.error) { showToast("error", "Rig action failed", res.error.detail ?? `Could not ${action} ${rig}`); @@ -395,7 +395,7 @@ async function ackEscalation(issue: BeadRecord): Promise { if (!city || !issue.id) return; const labels = Array.from(new Set([...(issue.labels ?? []), "acked"])); const res = await api.POST("/v0/city/{cityName}/bead/{id}/update", { - params: { path: { cityName: city, id: issue.id } }, + params: { path: { cityName: city, id: issue.id }, header: mutationHeaders }, body: { labels }, }); if (res.error) { @@ -410,7 +410,7 @@ async function closeBead(issueID: string): Promise { const city = cityScope(); if (!city) return; const res = await api.POST("/v0/city/{cityName}/bead/{id}/close", { - params: { path: { cityName: city, id: issueID } }, + params: { path: { cityName: city, id: issueID }, header: mutationHeaders }, }); if (res.error) { showToast("error", "Resolve failed", res.error.detail ?? "Could not resolve escalation"); @@ -431,7 +431,7 @@ async function reassignBead(issueID: string): Promise { }); if (!selection) return; const res = await api.POST("/v0/city/{cityName}/bead/{id}/assign", { - params: { path: { cityName: city, id: issueID } }, + params: { path: { cityName: city, id: issueID }, header: mutationHeaders }, body: { assignee: selection.target }, }); if (res.error) { diff --git a/cmd/gc/dashboard/web/src/panels/convoys.ts b/cmd/gc/dashboard/web/src/panels/convoys.ts index 87e158fc88..63478c919f 100644 --- a/cmd/gc/dashboard/web/src/panels/convoys.ts +++ b/cmd/gc/dashboard/web/src/panels/convoys.ts @@ -1,4 +1,4 @@ -import { api, cityScope } from "../api"; +import { api, cityScope, mutationHeaders } from "../api"; import { byId, clear, el } from "../util/dom"; import { calculateActivity, statusBadgeClass } from "../util/legacy"; import { popPause, pushPause, showToast } from "../ui"; @@ -298,7 +298,7 @@ async function createConvoy(): Promise { return; } const res = await api.POST("/v0/city/{cityName}/convoys", { - params: { path: { cityName: city } }, + params: { path: { cityName: city }, header: mutationHeaders }, body: { title, items }, }); if (res.error) { @@ -317,7 +317,7 @@ async function addIssueToConvoy(): Promise { const item = input?.value.trim() ?? ""; if (!item) return; const res = await api.POST("/v0/city/{cityName}/convoy/{id}/add", { - params: { path: { cityName: city, id: currentConvoyID } }, + params: { path: { cityName: city, id: currentConvoyID }, header: mutationHeaders }, body: { items: [item] }, }); if (res.error) { diff --git a/cmd/gc/dashboard/web/src/panels/issues.ts b/cmd/gc/dashboard/web/src/panels/issues.ts index 1c99685311..2997f0ecb2 100644 --- a/cmd/gc/dashboard/web/src/panels/issues.ts +++ b/cmd/gc/dashboard/web/src/panels/issues.ts @@ -1,5 +1,5 @@ import type { BeadRecord } from "../api"; -import { api, cityScope } from "../api"; +import { api, cityScope, mutationHeaders } from "../api"; import { promptActionDialog } from "../modals"; import { byId, clear, el } from "../util/dom"; import { beadPriority, formatTimestamp, priorityBadgeClass, truncate } from "../util/legacy"; @@ -347,7 +347,7 @@ async function closeIssue(issueID: string): Promise { const city = cityScope(); if (!city) return; const res = await api.POST("/v0/city/{cityName}/bead/{id}/close", { - params: { path: { cityName: city, id: issueID } }, + params: { path: { cityName: city, id: issueID }, header: mutationHeaders }, }); if (res.error) { showToast("error", "Close failed", res.error.detail ?? "Could not close issue"); @@ -362,7 +362,7 @@ async function reopenIssue(issueID: string): Promise { const city = cityScope(); if (!city) return; const res = await api.POST("/v0/city/{cityName}/bead/{id}/reopen", { - params: { path: { cityName: city, id: issueID } }, + params: { path: { cityName: city, id: issueID }, header: mutationHeaders }, }); if (res.error) { showToast("error", "Reopen failed", res.error.detail ?? "Could not reopen issue"); @@ -377,7 +377,7 @@ async function updateIssuePriority(issueID: string, priority: number): Promise { const city = cityScope(); if (!city) return; const res = await api.POST("/v0/city/{cityName}/bead/{id}/assign", { - params: { path: { cityName: city, id: issueID } }, + params: { path: { cityName: city, id: issueID }, header: mutationHeaders }, body: { assignee }, }); if (res.error) { @@ -416,7 +416,7 @@ async function slingIssue(issueID: string): Promise { }); if (!selection) return; const res = await api.POST("/v0/city/{cityName}/sling", { - params: { path: { cityName: city } }, + params: { path: { cityName: city }, header: mutationHeaders }, body: { bead: issueID, target: selection.target, rig: selection.rig || undefined }, }); if (res.error) { @@ -449,7 +449,7 @@ export async function createIssue(input: { const city = cityScope(); if (!city) return { ok: false, error: "no city selected" }; const { error } = await api.POST("/v0/city/{cityName}/beads", { - params: { path: { cityName: city } }, + params: { path: { cityName: city }, header: mutationHeaders }, body: { title: input.title, description: input.description, diff --git a/cmd/gc/dashboard/web/src/panels/mail.ts b/cmd/gc/dashboard/web/src/panels/mail.ts index 3b77727463..69c77a3a6e 100644 --- a/cmd/gc/dashboard/web/src/panels/mail.ts +++ b/cmd/gc/dashboard/web/src/panels/mail.ts @@ -1,5 +1,5 @@ import type { MailRecord } from "../api"; -import { api, cityScope } from "../api"; +import { api, cityScope, mutationHeaders } from "../api"; import { logError, logInfo, logWarn } from "../logger"; import { byId, clear, el } from "../util/dom"; import { formatAgentAddress, formatTimestamp } from "../util/legacy"; @@ -188,7 +188,7 @@ async function openMessage(messageID: string): Promise { } currentMessage = res.data; await api.POST("/v0/city/{cityName}/mail/{id}/read", { - params: { path: { cityName: city, id: messageID } }, + params: { path: { cityName: city, id: messageID }, header: mutationHeaders }, }); currentMessage.read = true; showMailDetail(currentMessage, [currentMessage]); @@ -356,11 +356,11 @@ async function sendCurrentMessage(): Promise { const response = replyTo ? await api.POST("/v0/city/{cityName}/mail/{id}/reply", { - params: { path: { cityName: city, id: replyTo } }, + params: { path: { cityName: city, id: replyTo }, header: mutationHeaders }, body: { body, subject }, }) : await api.POST("/v0/city/{cityName}/mail", { - params: { path: { cityName: city } }, + params: { path: { cityName: city }, header: mutationHeaders }, body: { to, subject, body, from: "dashboard" }, }); @@ -396,7 +396,7 @@ async function archiveMessage(id: string): Promise { const city = cityScope(); if (!city) return; const res = await api.POST("/v0/city/{cityName}/mail/{id}/archive", { - params: { path: { cityName: city, id } }, + params: { path: { cityName: city, id }, header: mutationHeaders }, }); if (res.error) { showToast("error", "Archive failed", res.error.detail ?? "Could not archive message"); @@ -417,7 +417,7 @@ async function toggleUnread(message: MailRecord): Promise { ? "/v0/city/{cityName}/mail/{id}/mark-unread" : "/v0/city/{cityName}/mail/{id}/read"; const res = await api.POST(route, { - params: { path: { cityName: city, id: message.id } }, + params: { path: { cityName: city, id: message.id }, header: mutationHeaders }, }); if (res.error) { showToast("error", "Update failed", res.error.detail ?? "Could not update message"); diff --git a/cmd/gc/dashboard/web/src/sse.ts b/cmd/gc/dashboard/web/src/sse.ts index 37c764321b..98995e3eae 100644 --- a/cmd/gc/dashboard/web/src/sse.ts +++ b/cmd/gc/dashboard/web/src/sse.ts @@ -15,7 +15,7 @@ // narrow on actual runtime shape and drop malformed frames with a // UI error report — same discipline as the pre-migration decoder. // -// See specs/architecture.md §6 "Tooling landscape" (TypeScript +// See engdocs/architecture/api-control-plane.md §6 "Tooling landscape" (TypeScript // section) for why hey-api is the SSE tool even though openapi-fetch // still drives REST. diff --git a/cmd/gc/dolt_gc_nudge_script_test.go b/cmd/gc/dolt_gc_nudge_script_test.go index 43af1bf2a2..c4c32bf63f 100644 --- a/cmd/gc/dolt_gc_nudge_script_test.go +++ b/cmd/gc/dolt_gc_nudge_script_test.go @@ -286,6 +286,7 @@ func TestDoltGCNudgeDefaultCallTimeoutMatchesOrderBudget(t *testing.T) { } func TestDoltGCNudgeBoundsGCCall(t *testing.T) { + skipSlowCmdGCTest(t, "runs dolt GC nudge shell timeout coverage; run make test-cmd-gc-process for full coverage") if _, err := exec.LookPath("timeout"); err != nil { if _, gtimeoutErr := exec.LookPath("gtimeout"); gtimeoutErr != nil { t.Skip("timeout/gtimeout not available") @@ -344,6 +345,7 @@ func TestDoltGCNudgeFailsClosedWithoutBoundedRunner(t *testing.T) { } func TestDoltGCNudgeFallbackLockHonorsFlockHolder(t *testing.T) { + skipSlowCmdGCTest(t, "runs dolt GC nudge shell lock contention coverage; run make test-cmd-gc-process for full coverage") cityPath := writeDoltGCNudgeCity(t) sleepPath, err := exec.LookPath("sleep") if err != nil { @@ -396,6 +398,7 @@ func TestDoltGCNudgeFallbackLockHonorsFlockHolder(t *testing.T) { } func TestDoltGCNudgeLockNormalizesLocalHostAliases(t *testing.T) { + skipSlowCmdGCTest(t, "runs dolt GC nudge shell lock contention coverage; run make test-cmd-gc-process for full coverage") cityPath := writeDoltGCNudgeCity(t) sleepPath, err := exec.LookPath("sleep") if err != nil { @@ -453,6 +456,7 @@ func TestDoltGCNudgeLockNormalizesLocalHostAliases(t *testing.T) { } func TestDoltGCNudgeLockIgnoresDifferentTmpDirs(t *testing.T) { + skipSlowCmdGCTest(t, "runs dolt GC nudge shell lock contention coverage; run make test-cmd-gc-process for full coverage") cityPath := writeDoltGCNudgeCity(t) sleepPath, err := exec.LookPath("sleep") if err != nil { @@ -593,10 +597,10 @@ func TestDoltGCNudgeSkipsExternalRigDatabaseWithoutLocalData(t *testing.T) { if len(lines) != 1 { t.Fatalf("dolt argv lines = %d, want 1 for local managed db only:\n%s", len(lines), argv) } - if !strings.Contains(lines[0], "--database testdb") { + if !strings.Contains(lines[0], "--use-db testdb") { t.Fatalf("dolt argv = %q, want local managed testdb", lines[0]) } - if strings.Contains(argv, "--database extdb") { + if strings.Contains(argv, "--use-db extdb") { t.Fatalf("dolt argv should not target external rig db:\n%s", argv) } } @@ -630,7 +634,7 @@ func TestDoltGCNudgeDefaultsMissingDatabaseMetadataToBeads(t *testing.T) { } argv := strings.TrimSpace(readFileString(t, argvCapture)) - if !strings.Contains(argv, "--database beads") { + if !strings.Contains(argv, "--use-db beads") { t.Fatalf("dolt argv = %q, want default beads database", argv) } } @@ -669,10 +673,10 @@ func TestDoltGCNudgeSkipsInvalidDatabaseMetadata(t *testing.T) { if len(lines) != 1 { t.Fatalf("dolt argv lines = %d, want 1 valid database:\n%s", len(lines), argv) } - if !strings.Contains(lines[0], "--database testdb") { + if !strings.Contains(lines[0], "--use-db testdb") { t.Fatalf("dolt argv = %q, want local managed testdb", lines[0]) } - if strings.Contains(argv, "--database --help") { + if strings.Contains(argv, "--use-db --help") { t.Fatalf("dolt argv should not target invalid database:\n%s", argv) } } @@ -710,10 +714,10 @@ func TestDoltGCNudgeSkipsSystemDatabaseMetadata(t *testing.T) { } argv := strings.TrimSpace(readFileString(t, argvCapture)) - if strings.Contains(argv, "--database mysql") { + if strings.Contains(argv, "--use-db mysql") { t.Fatalf("dolt argv should not target system database:\n%s", argv) } - if !strings.Contains(argv, "--database testdb") { + if !strings.Contains(argv, "--use-db testdb") { t.Fatalf("dolt argv = %q, want valid testdb", argv) } } @@ -747,7 +751,7 @@ func TestDoltGCNudgeAllowsHyphenatedDatabaseMetadata(t *testing.T) { } argv := strings.TrimSpace(readFileString(t, argvCapture)) - if !strings.Contains(argv, "--database frontend-db") { + if !strings.Contains(argv, "--use-db frontend-db") { t.Fatalf("dolt argv = %q, want hyphenated database", argv) } } @@ -786,7 +790,7 @@ func TestDoltGCNudgeHonorsDataDirOverride(t *testing.T) { } argv := strings.TrimSpace(readFileString(t, argvCapture)) - if !strings.Contains(argv, "--database testdb") { + if !strings.Contains(argv, "--use-db testdb") { t.Fatalf("dolt argv = %q, want override-backed testdb", argv) } } @@ -817,7 +821,7 @@ func TestDoltGCNudgeDiscoversOrphanDatabaseDirs(t *testing.T) { } argv := strings.TrimSpace(readFileString(t, argvCapture)) - if !strings.Contains(argv, "--database orphan-db") { + if !strings.Contains(argv, "--use-db orphan-db") { t.Fatalf("dolt argv = %q, want orphan database", argv) } } @@ -869,7 +873,7 @@ func TestDoltGCNudgeAggregateThresholdTriggersSubthresholdDatabases(t *testing.T } argv := strings.TrimSpace(readFileString(t, argvCapture)) - if !strings.Contains(argv, "--database testdb") || !strings.Contains(argv, "--database rigdb") { + if !strings.Contains(argv, "--use-db testdb") || !strings.Contains(argv, "--use-db rigdb") { t.Fatalf("dolt argv = %q, want both subthreshold databases under aggregate trigger", argv) } } @@ -911,10 +915,10 @@ func TestDoltGCNudgeFallbackFindsLocalRigOutsideRigsDir(t *testing.T) { if len(lines) != 2 { t.Fatalf("dolt argv lines = %d, want 2 databases from fallback scan:\n%s", len(lines), argv) } - if !strings.Contains(argv, "--database testdb") { + if !strings.Contains(argv, "--use-db testdb") { t.Fatalf("dolt argv = %q, want city database", argv) } - if !strings.Contains(argv, "--database frontenddb") { + if !strings.Contains(argv, "--use-db frontenddb") { t.Fatalf("dolt argv = %q, want rig database outside rigs/ dir", argv) } } @@ -940,7 +944,7 @@ func TestDoltGCNudgeWarnsWhenRigListFailsBeforeFallback(t *testing.T) { if !strings.Contains(string(out), "gc rig list failed rc=7") { t.Fatalf("gc-nudge output = %q, want rig-list failure warning", out) } - if !strings.Contains(readFileString(t, argvCapture), "--database testdb") { + if !strings.Contains(readFileString(t, argvCapture), "--use-db testdb") { t.Fatalf("gc-nudge did not fall back to local metadata scan; output:\n%s", out) } } diff --git a/cmd/gc/dolt_preflight_cleanup_test.go b/cmd/gc/dolt_preflight_cleanup_test.go index 1e3c9a1085..0d9f3b0be1 100644 --- a/cmd/gc/dolt_preflight_cleanup_test.go +++ b/cmd/gc/dolt_preflight_cleanup_test.go @@ -83,6 +83,7 @@ func TestFileOpenedByAnyProcessBoundsLsof(t *testing.T) { } func TestRemoveStaleManagedDoltLocksWithoutLsofUsesAvailableState(t *testing.T) { + skipSlowCmdGCTest(t, "runs managed-dolt preflight cleanup against filesystem locks; run make test-cmd-gc-process for full coverage") dataDir := t.TempDir() lockFile := filepath.Join(dataDir, "hq", ".dolt", "noms", "LOCK") if err := os.MkdirAll(filepath.Dir(lockFile), 0o755); err != nil { diff --git a/cmd/gc/dolt_project_id_test.go b/cmd/gc/dolt_project_id_test.go index 32d7ba50dc..0a389722a1 100644 --- a/cmd/gc/dolt_project_id_test.go +++ b/cmd/gc/dolt_project_id_test.go @@ -14,7 +14,7 @@ import ( ) func TestEnsureManagedDoltProjectIDGeneratesLocalIdentityWhenMetadataAndDatabaseMissing(t *testing.T) { - skipSlowCmdGCTest(t, "requires a managed dolt server; run without -short or via integration packages") + skipSlowCmdGCTest(t, "requires a managed dolt server; run make test-cmd-gc-process for full coverage") doltPath := os.Getenv("GC_DOLT_REAL_BINARY") var err error if doltPath == "" { @@ -258,7 +258,7 @@ func TestManagedDoltWaitReadyWithPasswordUsesDirectQueryProbe(t *testing.T) { } func TestRecoverManagedDoltProcessWithPasswordUsesDirectHelpersAgainstRealServer(t *testing.T) { - skipSlowCmdGCTest(t, "requires a managed dolt server; run without -short or via integration packages") + skipSlowCmdGCTest(t, "requires a managed dolt server; run make test-cmd-gc-process for full coverage") cityPath := t.TempDir() layout, err := resolveManagedDoltRuntimeLayout(cityPath) if err != nil { @@ -305,7 +305,7 @@ func TestRecoverManagedDoltProcessWithPasswordUsesDirectHelpersAgainstRealServer } func TestEnsureManagedDoltProjectIDGeneratesLocalIdentityWithPasswordedServer(t *testing.T) { - skipSlowCmdGCTest(t, "requires a managed dolt server; run without -short or via integration packages") + skipSlowCmdGCTest(t, "requires a managed dolt server; run make test-cmd-gc-process for full coverage") cityDir := t.TempDir() metadataPath := filepath.Join(cityDir, ".beads", "metadata.json") if err := os.MkdirAll(filepath.Dir(metadataPath), 0o755); err != nil { diff --git a/cmd/gc/dolt_start_managed.go b/cmd/gc/dolt_start_managed.go index fc4e581a01..4f7431142b 100644 --- a/cmd/gc/dolt_start_managed.go +++ b/cmd/gc/dolt_start_managed.go @@ -73,6 +73,7 @@ func startManagedDoltProcessWithOptions(cityPath, host, port, user, logLevel str cmd.Stderr = logFile cmd.Stdin = nil cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + cmd.Env = doltServerEnv(os.Environ()) if err := cmd.Start(); err != nil { _ = logFile.Close() return report, fmt.Errorf("start dolt sql-server: %w", err) @@ -208,3 +209,20 @@ func terminateManagedDoltPID(pid int) error { time.Sleep(250 * time.Millisecond) return nil } + +// doltServerEnv augments the parent environment with overrides we need +// applied to every managed dolt sql-server we launch. Currently it +// disables Dolt's load-average auto-GC scheduler, which on multi-core +// hosts (>~16 CPUs) silently prevents auto-GC from ever running. See +// https://github.com/dolthub/dolt/issues/10944. Users who explicitly +// set DOLT_GC_SCHEDULER are respected. +func doltServerEnv(parent []string) []string { + const key = "DOLT_GC_SCHEDULER" + prefix := key + "=" + for _, kv := range parent { + if strings.HasPrefix(kv, prefix) { + return parent + } + } + return append(append([]string(nil), parent...), prefix+"NONE") +} diff --git a/cmd/gc/dolt_start_managed_test.go b/cmd/gc/dolt_start_managed_test.go new file mode 100644 index 0000000000..a7058f93ac --- /dev/null +++ b/cmd/gc/dolt_start_managed_test.go @@ -0,0 +1,90 @@ +package main + +import ( + "os" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func TestDoltServerEnv_AppendsDefaultWhenMissing(t *testing.T) { + parent := []string{"PATH=/usr/bin", "HOME=/home/test"} + out := doltServerEnv(parent) + + want := "DOLT_GC_SCHEDULER=NONE" + found := false + for _, kv := range out { + if kv == want { + found = true + break + } + } + if !found { + t.Fatalf("expected %q in env, got %v", want, out) + } + // Original entries preserved. + for _, kv := range parent { + var hit bool + for _, got := range out { + if got == kv { + hit = true + break + } + } + if !hit { + t.Fatalf("parent entry %q missing from output env %v", kv, out) + } + } +} + +func TestDoltServerEnv_RespectsUserOverride(t *testing.T) { + parent := []string{"PATH=/usr/bin", "DOLT_GC_SCHEDULER=LOADAVG", "HOME=/home/test"} + out := doltServerEnv(parent) + + // User-provided value must be preserved exactly. + count := 0 + for _, kv := range out { + if kv == "DOLT_GC_SCHEDULER=LOADAVG" { + count++ + } + if kv == "DOLT_GC_SCHEDULER=NONE" { + t.Fatalf("user override clobbered by default: %v", out) + } + } + if count != 1 { + t.Fatalf("expected exactly one DOLT_GC_SCHEDULER=LOADAVG entry, got %d in %v", count, out) + } +} + +func TestDoltServerEnv_RespectsEmptyUserValue(t *testing.T) { + // An explicit empty value (DOLT_GC_SCHEDULER=) is still a user + // override and we must not replace it. + parent := []string{"DOLT_GC_SCHEDULER="} + out := doltServerEnv(parent) + for _, kv := range out { + if kv == "DOLT_GC_SCHEDULER=NONE" { + t.Fatalf("explicit empty-value override clobbered: %v", out) + } + } +} + +func TestGCBeadsBDScript_RespectsEmptyUserValue(t *testing.T) { + _, thisFile, _, ok := runtime.Caller(0) + if !ok { + t.Fatal("runtime.Caller(0) failed") + } + scriptPath := filepath.Join(filepath.Dir(thisFile), "..", "..", "examples", "bd", "assets", "scripts", "gc-beads-bd.sh") + data, err := os.ReadFile(scriptPath) + if err != nil { + t.Fatalf("read %s: %v", scriptPath, err) + } + script := string(data) + + if !strings.Contains(script, `${DOLT_GC_SCHEDULER=NONE}`) { + t.Fatalf("gc-beads-bd.sh must default DOLT_GC_SCHEDULER only when unset") + } + if strings.Contains(script, `${DOLT_GC_SCHEDULER:=NONE}`) { + t.Fatalf("gc-beads-bd.sh must not clobber an explicitly empty DOLT_GC_SCHEDULER") + } +} diff --git a/cmd/gc/embed_builtin_packs.go b/cmd/gc/embed_builtin_packs.go index fc5f223635..d55fbf1c33 100644 --- a/cmd/gc/embed_builtin_packs.go +++ b/cmd/gc/embed_builtin_packs.go @@ -14,6 +14,7 @@ import ( "github.com/gastownhall/gascity/examples/gastown/packs/maintenance" "github.com/gastownhall/gascity/internal/bootstrap/packs/core" "github.com/gastownhall/gascity/internal/citylayout" + "github.com/gastownhall/gascity/internal/fsys" "github.com/gastownhall/gascity/internal/orders" ) @@ -38,9 +39,10 @@ var builtinPacks = []builtinPack{ } // MaterializeBuiltinPacks writes all embedded pack files to -// .gc/system/packs/{name}/ in the city directory. Files are always -// overwritten to stay in sync with the gc binary version. Shell scripts -// get 0755; everything else 0644. +// .gc/system/packs/{name}/ in the city directory. Files whose content and mode +// already match are left in place; changed content or mode is repaired with an +// atomic rename so readers never observe a truncated file. Shell scripts get +// 0755; everything else 0644. // Idempotent: safe to call on every gc start and gc init. func MaterializeBuiltinPacks(cityPath string) error { for _, bp := range builtinPacks { @@ -160,7 +162,7 @@ func materializeFS(embedded fs.FS, root, dstDir string) error { if isExecutableScriptFilename(path) { perm = 0o755 } - return os.WriteFile(dst, data, perm) + return fsys.WriteFileIfContentOrModeChangedAtomic(fsys.OSFS{}, dst, data, perm) }) } diff --git a/cmd/gc/embed_builtin_packs_test.go b/cmd/gc/embed_builtin_packs_test.go index 1530459ee4..a93035b0f7 100644 --- a/cmd/gc/embed_builtin_packs_test.go +++ b/cmd/gc/embed_builtin_packs_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" "testing" + "time" "github.com/gastownhall/gascity/internal/citylayout" "github.com/gastownhall/gascity/internal/config" @@ -325,6 +326,93 @@ func TestMaterializeBuiltinPacks_Idempotent(t *testing.T) { } } +func TestMaterializeBuiltinPacks_DoesNotRewriteUnchangedFiles(t *testing.T) { + dir := t.TempDir() + + if err := MaterializeBuiltinPacks(dir); err != nil { + t.Fatalf("MaterializeBuiltinPacks() error: %v", err) + } + + path := filepath.Join(dir, citylayout.SystemPacksRoot, "core", "skills", "gc-dashboard", "SKILL.md") + past := time.Unix(123456789, 0) + if err := os.Chtimes(path, past, past); err != nil { + t.Fatalf("Chtimes(%s): %v", path, err) + } + + if err := MaterializeBuiltinPacks(dir); err != nil { + t.Fatalf("MaterializeBuiltinPacks() second call error: %v", err) + } + + info, err := os.Stat(path) + if err != nil { + t.Fatalf("Stat(%s): %v", path, err) + } + if !info.ModTime().Equal(past) { + t.Fatalf("unchanged file was rewritten: modtime = %s, want %s", info.ModTime(), past) + } +} + +func TestMaterializeBuiltinPacks_RestoresModeWhenContentUnchanged(t *testing.T) { + dir := t.TempDir() + + if err := MaterializeBuiltinPacks(dir); err != nil { + t.Fatalf("MaterializeBuiltinPacks() error: %v", err) + } + + path := filepath.Join(dir, citylayout.SystemPacksRoot, "bd", "doctor", "check-bd", "run.sh") + if err := os.Chmod(path, 0o644); err != nil { + t.Fatalf("Chmod(%s): %v", path, err) + } + + if err := MaterializeBuiltinPacks(dir); err != nil { + t.Fatalf("MaterializeBuiltinPacks() second call error: %v", err) + } + + info, err := os.Stat(path) + if err != nil { + t.Fatalf("Stat(%s): %v", path, err) + } + if info.Mode().Perm() != 0o755 { + t.Fatalf("script mode was not restored: %v", info.Mode().Perm()) + } +} + +func TestMaterializeBuiltinPacks_ReplacesMatchingSymlink(t *testing.T) { + dir := t.TempDir() + + if err := MaterializeBuiltinPacks(dir); err != nil { + t.Fatalf("MaterializeBuiltinPacks() error: %v", err) + } + + path := filepath.Join(dir, citylayout.SystemPacksRoot, "core", "skills", "gc-dashboard", "SKILL.md") + data, err := os.ReadFile(path) + if err != nil { + t.Fatalf("ReadFile(%s): %v", path, err) + } + target := filepath.Join(dir, "outside-skill.md") + if err := os.WriteFile(target, data, 0o644); err != nil { + t.Fatalf("WriteFile(%s): %v", target, err) + } + if err := os.Remove(path); err != nil { + t.Fatalf("Remove(%s): %v", path, err) + } + if err := os.Symlink(target, path); err != nil { + t.Skipf("Symlink: %v", err) + } + + if err := MaterializeBuiltinPacks(dir); err != nil { + t.Fatalf("MaterializeBuiltinPacks() second call error: %v", err) + } + + info, err := os.Lstat(path) + if err != nil { + t.Fatalf("Lstat(%s): %v", path, err) + } + if info.Mode()&os.ModeSymlink != 0 { + t.Fatalf("matching symlink was preserved, want regular file") + } +} + func TestMaterializedBuiltinPackOrdersScanWithoutWarnings(t *testing.T) { dir := t.TempDir() diff --git a/cmd/gc/fast_loop_helpers_test.go b/cmd/gc/fast_loop_helpers_test.go index 9ecd5ba247..0e92f2f36d 100644 --- a/cmd/gc/fast_loop_helpers_test.go +++ b/cmd/gc/fast_loop_helpers_test.go @@ -14,7 +14,10 @@ import ( func skipSlowCmdGCTest(t *testing.T, reason string) { t.Helper() - if os.Getenv("GC_FAST_UNIT") == "1" || testing.Short() { + if testing.Short() || strings.TrimSpace(os.Getenv("GC_FAST_UNIT")) != "0" { + if strings.TrimSpace(os.Getenv("GC_FAST_UNIT")) == "" && !strings.Contains(reason, "test-cmd-gc-process") { + reason += "; set GC_FAST_UNIT=0 or run make test-cmd-gc-process for full process coverage" + } t.Skip(reason) } } @@ -80,6 +83,7 @@ func reserveRandomTCPPort(t *testing.T) int { func startTCPListenerProcess(t *testing.T, port int) *exec.Cmd { t.Helper() + skipSlowCmdGCTest(t, "spawns a TCP listener process to emulate managed dolt; run make test-cmd-gc-process for full coverage") cmd := exec.Command("python3", "-c", ` import signal import socket diff --git a/cmd/gc/hooks.go b/cmd/gc/hooks.go index b2c7b37456..48d0764845 100644 --- a/cmd/gc/hooks.go +++ b/cmd/gc/hooks.go @@ -4,6 +4,8 @@ import ( "fmt" "os" "path/filepath" + + "github.com/gastownhall/gascity/internal/fsys" ) // beadHooks maps bd hook filenames to the Gas City event types they emit. @@ -53,7 +55,7 @@ title=$(echo "$DATA" | grep -o '"title":"[^"]*"' | head -1 | cut -d'"' -f4) // installBeadHooks writes bd hook scripts into dir/.beads/hooks/ so that // bd mutations (create, close, update) emit events to the Gas City event -// log. Idempotent — overwrites existing hooks. Returns nil on success. +// log. Idempotent — leaves matching hooks in place. Returns nil on success. func installBeadHooks(dir string) error { hooksDir := filepath.Join(dir, ".beads", "hooks") if err := os.MkdirAll(hooksDir, 0o755); err != nil { @@ -66,7 +68,7 @@ func installBeadHooks(dir string) error { if filename == "on_close" { content = closeHookScript() } - if err := os.WriteFile(path, []byte(content), 0o755); err != nil { + if err := fsys.WriteFileIfContentOrModeChangedAtomic(fsys.OSFS{}, path, []byte(content), 0o755); err != nil { return fmt.Errorf("writing hook %s: %w", filename, err) } } diff --git a/cmd/gc/hooks_test.go b/cmd/gc/hooks_test.go index 1c263acff9..be3034c531 100644 --- a/cmd/gc/hooks_test.go +++ b/cmd/gc/hooks_test.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" "testing" + "time" ) func TestInstallBeadHooksCreatesScripts(t *testing.T) { @@ -98,6 +99,68 @@ func TestInstallBeadHooksIdempotent(t *testing.T) { } } +func TestInstallBeadHooksDoesNotRewriteUnchangedHooks(t *testing.T) { + dir := t.TempDir() + + if err := installBeadHooks(dir); err != nil { + t.Fatalf("first install: %v", err) + } + + path := filepath.Join(dir, ".beads", "hooks", "on_create") + past := time.Unix(123456789, 0) + if err := os.Chtimes(path, past, past); err != nil { + t.Fatalf("Chtimes: %v", err) + } + + if err := installBeadHooks(dir); err != nil { + t.Fatalf("second install: %v", err) + } + + info, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + if !info.ModTime().Equal(past) { + t.Fatalf("unchanged hook was rewritten: modtime = %s, want %s", info.ModTime(), past) + } +} + +func TestInstallBeadHooksReplacesMatchingSymlink(t *testing.T) { + dir := t.TempDir() + + if err := installBeadHooks(dir); err != nil { + t.Fatalf("first install: %v", err) + } + + path := filepath.Join(dir, ".beads", "hooks", "on_create") + data, err := os.ReadFile(path) + if err != nil { + t.Fatalf("ReadFile(%s): %v", path, err) + } + target := filepath.Join(dir, "outside-hook") + if err := os.WriteFile(target, data, 0o755); err != nil { + t.Fatalf("WriteFile(%s): %v", target, err) + } + if err := os.Remove(path); err != nil { + t.Fatalf("Remove(%s): %v", path, err) + } + if err := os.Symlink(target, path); err != nil { + t.Skipf("Symlink: %v", err) + } + + if err := installBeadHooks(dir); err != nil { + t.Fatalf("second install: %v", err) + } + + info, err := os.Lstat(path) + if err != nil { + t.Fatalf("Lstat(%s): %v", path, err) + } + if info.Mode()&os.ModeSymlink != 0 { + t.Fatalf("matching symlink was preserved, want regular file") + } +} + func TestInstallBeadHooksCreatesDirectories(t *testing.T) { dir := t.TempDir() // No pre-existing .beads/ directory. diff --git a/cmd/gc/live_submit_probe_test.go b/cmd/gc/live_submit_probe_test.go index 1043ab4f7c..f44f8c0c45 100644 --- a/cmd/gc/live_submit_probe_test.go +++ b/cmd/gc/live_submit_probe_test.go @@ -21,7 +21,7 @@ import ( func preferRealBDOnPath(t *testing.T) { t.Helper() - skipSlowCmdGCTest(t, "requires a live bd-managed session probe; run without -short") + skipSlowCmdGCTest(t, "requires a live bd-managed session probe; run make test-cmd-gc-process for full coverage") currentPath := os.Getenv("PATH") pathEntries := filepath.SplitList(currentPath) diff --git a/cmd/gc/main.go b/cmd/gc/main.go index a092f19bb4..73a0f7cccf 100644 --- a/cmd/gc/main.go +++ b/cmd/gc/main.go @@ -275,6 +275,9 @@ var cliStoreCache struct { // agents don't open the store repeatedly. Silently falls back to legacy // naming if the store is unavailable. func cliSessionName(cityPath, cityName, agentName, sessionTemplate string) string { + if strings.TrimSpace(cityPath) == "" { + return sessionName(nil, cityName, agentName, sessionTemplate) + } cliStoreCache.mu.Lock() if cliStoreCache.path != cityPath { cliStoreCache.store, _ = openCityStoreAt(cityPath) diff --git a/cmd/gc/main_test.go b/cmd/gc/main_test.go index f081ed0d35..8e5387c642 100644 --- a/cmd/gc/main_test.go +++ b/cmd/gc/main_test.go @@ -160,6 +160,7 @@ func TestMain(m *testing.M) { } func TestTutorial01(t *testing.T) { + skipSlowCmdGCTest(t, "runs tutorial testscript scenarios; run make test-cmd-gc-process for full coverage") testscript.Run(t, newTestscriptParams(t)) } diff --git a/cmd/gc/mcp_integration.go b/cmd/gc/mcp_integration.go index 03b7ed518a..906964f2b6 100644 --- a/cmd/gc/mcp_integration.go +++ b/cmd/gc/mcp_integration.go @@ -38,24 +38,6 @@ type resolvedMCPProjection struct { Projection materialize.MCPProjection } -func buildMCPTemplateData(cityPath, qualifiedName, workDir string, agent *config.Agent, rigs []config.Rig) map[string]string { - rigName := configuredRigName(cityPath, agent, rigs) - rigRoot := rigRootForName(rigName, rigs) - return buildTemplateData(PromptContext{ - CityRoot: cityPath, - AgentName: qualifiedName, - TemplateName: templateNameFor(agent, qualifiedName), - RigName: rigName, - RigRoot: rigRoot, - WorkDir: workDir, - IssuePrefix: findRigPrefix(rigName, rigs), - DefaultBranch: defaultBranchFor(workDir), - WorkQuery: agent.EffectiveWorkQuery(), - SlingQuery: agent.EffectiveSlingQuery(), - Env: agent.Env, - }) -} - func supportsMCPProviderKind(kind string) bool { switch strings.TrimSpace(kind) { case materialize.MCPProviderClaude, materialize.MCPProviderCodex, materialize.MCPProviderGemini: @@ -71,8 +53,7 @@ func loadEffectiveMCPForAgent( agent *config.Agent, qualifiedName, workDir string, ) (materialize.MCPCatalog, error) { - templateData := buildMCPTemplateData(cityPath, qualifiedName, workDir, agent, cfg.Rigs) - catalog, err := materialize.EffectiveMCPForAgent(cfg, agent, templateData) + catalog, err := materialize.EffectiveMCPForSession(cfg, cityPath, agent, qualifiedName, workDir) if err != nil { return materialize.MCPCatalog{}, fmt.Errorf("loading effective MCP: %w", err) } diff --git a/cmd/gc/phase2_reporting_test.go b/cmd/gc/phase2_reporting_test.go index 320b2d47a9..63de8f1236 100644 --- a/cmd/gc/phase2_reporting_test.go +++ b/cmd/gc/phase2_reporting_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/gastownhall/gascity/internal/config" "github.com/gastownhall/gascity/internal/runtime" workertest "github.com/gastownhall/gascity/internal/worker/workertest" ) @@ -178,11 +179,11 @@ func inputOverrideDefaultsResult(tc phase2ProviderCase, prepared *preparedStart) return workertest.Fail(tc.profileID, workertest.RequirementInputOverrideDefaults, "ResolvedProvider = nil, want provider defaults for override comparison").WithEvidence(evidence) } - defaultArgs := prepared.candidate.tp.ResolvedProvider.ResolveDefaultArgs() + defaultArgs := defaultArgsExceptOption(prepared.candidate.tp.ResolvedProvider, "model") switch { case !containsOrderedArgs(prepared.cfg.Command, defaultArgs): return workertest.Fail(tc.profileID, workertest.RequirementInputOverrideDefaults, - fmt.Sprintf("Command = %q, want default args %v", prepared.cfg.Command, defaultArgs)).WithEvidence(evidence) + fmt.Sprintf("Command = %q, want non-model default args %v", prepared.cfg.Command, defaultArgs)).WithEvidence(evidence) case !containsOrderedArgs(prepared.cfg.Command, tc.wantModelOverrideArgs): return workertest.Fail(tc.profileID, workertest.RequirementInputOverrideDefaults, fmt.Sprintf("Command = %q, want model override args %v", prepared.cfg.Command, tc.wantModelOverrideArgs)).WithEvidence(evidence) @@ -198,6 +199,49 @@ func inputOverrideDefaultsResult(tc phase2ProviderCase, prepared *preparedStart) } } +func defaultArgsExceptOption(provider *config.ResolvedProvider, optionKey string) []string { + if provider == nil { + return nil + } + defaultArgs := provider.ResolveDefaultArgs() + defaultValue := provider.EffectiveDefaults[optionKey] + for _, opt := range provider.OptionsSchema { + if opt.Key == optionKey && defaultValue == "" { + defaultValue = opt.Default + } + if opt.Key != optionKey || defaultValue == "" { + continue + } + for _, choice := range opt.Choices { + if choice.Value == defaultValue { + return removeContiguousArgs(defaultArgs, choice.FlagArgs) + } + } + } + return defaultArgs +} + +func removeContiguousArgs(args, remove []string) []string { + if len(args) == 0 || len(remove) == 0 || len(remove) > len(args) { + return args + } + for i := 0; i <= len(args)-len(remove); i++ { + matched := true + for j := range remove { + if args[i+j] != remove[j] { + matched = false + break + } + } + if matched { + out := append([]string{}, args[:i]...) + out = append(out, args[i+len(remove):]...) + return out + } + } + return args +} + func phase2TemplateEvidence(tc phase2ProviderCase, tp TemplateParams) map[string]string { evidence := map[string]string{ "family": tc.family, diff --git a/cmd/gc/pool_test.go b/cmd/gc/pool_test.go index ff4bff01c5..b5e8ffa3af 100644 --- a/cmd/gc/pool_test.go +++ b/cmd/gc/pool_test.go @@ -101,6 +101,7 @@ func TestEvaluatePoolNonInteger(t *testing.T) { } func TestEvaluatePoolDefaultScaleCheckCountsRoutedReadyWork(t *testing.T) { + skipSlowCmdGCTest(t, "uses real bd and jq for default scale_check coverage; run make test-cmd-gc-process for full coverage") bdPath, err := findPreferredBinary("bd", "/home/ubuntu/.local/bin/bd") if err != nil { t.Skip("bd not installed") @@ -144,6 +145,7 @@ func TestEvaluatePoolDefaultScaleCheckCountsRoutedReadyWork(t *testing.T) { } func TestEvaluatePoolDefaultScaleCheckCountsRoutedActiveUnassignedWork(t *testing.T) { + skipSlowCmdGCTest(t, "uses real bd and jq for default scale_check coverage; run make test-cmd-gc-process for full coverage") bdPath, err := findPreferredBinary("bd", "/home/ubuntu/.local/bin/bd") if err != nil { t.Skip("bd not installed") diff --git a/cmd/gc/providers.go b/cmd/gc/providers.go index 5e9f77b9b9..91320a27c4 100644 --- a/cmd/gc/providers.go +++ b/cmd/gc/providers.go @@ -26,6 +26,7 @@ import ( sessionk8s "github.com/gastownhall/gascity/internal/runtime/k8s" sessionsubprocess "github.com/gastownhall/gascity/internal/runtime/subprocess" sessiontmux "github.com/gastownhall/gascity/internal/runtime/tmux" + "github.com/gastownhall/gascity/internal/session" "github.com/gastownhall/gascity/internal/supervisor" ) @@ -70,7 +71,10 @@ func sessionProviderContextForCity(cfg *config.City, cityPath, providerOverride return ctx } -var openSessionProviderStore = openCityStoreAt +var ( + openSessionProviderStore = openCityStoreAt + buildSessionProviderByName = newSessionProviderByName +) // tmuxConfigFromSession converts a config.SessionConfig into a // sessiontmux.Config with resolved durations and defaults. If the @@ -161,7 +165,7 @@ func newSessionProviderForCity(cfg *config.City, cityPath string) runtime.Provid } func loadProviderSessionSnapshot(ctx sessionProviderContext) *sessionBeadSnapshot { - if ctx.cityPath == "" || ctx.providerName == "acp" || !hasACPAgents(ctx.agents) { + if ctx.cityPath == "" || ctx.providerName == "acp" { return nil } store, err := openSessionProviderStore(ctx.cityPath) @@ -176,47 +180,64 @@ func loadProviderSessionSnapshot(ctx sessionProviderContext) *sessionBeadSnapsho } func newSessionProviderFromContext(ctx sessionProviderContext, sessionBeads *sessionBeadSnapshot) runtime.Provider { - sp, err := newSessionProviderByName(ctx.providerName, ctx.sc, ctx.cityName, ctx.cityPath) + sp, err := newSessionProviderFromContextWithError(ctx, sessionBeads) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) //nolint:errcheck // best-effort stderr os.Exit(1) } + return sp +} + +func newSessionProviderFromContextWithError(ctx sessionProviderContext, sessionBeads *sessionBeadSnapshot) (runtime.Provider, error) { + sp, err := newSessionProviderByName(ctx.providerName, ctx.sc, ctx.cityName, ctx.cityPath) + if err != nil { + return nil, err + } // If the city-level provider is not ACP but some agents need ACP, // wrap in an auto provider that routes per-session. // NOTE: agents comes from loadCityConfig which applies pack overrides, // so the Session field from overrides is already resolved here. - if ctx.providerName != "acp" && hasACPAgents(ctx.agents) { - acpSP, acpErr := newSessionProviderByName("acp", ctx.sc, ctx.cityName, ctx.cityPath) + requireACPWrapper := requiresACPProviderWrapper(sessionBeads, ctx.cityName, ctx.cfg) + if ctx.providerName != "acp" && needsACPProviderWrapper(sessionBeads, ctx.cityName, ctx.cfg) { + acpSP, acpErr := buildSessionProviderByName("acp", ctx.sc, ctx.cityName, ctx.cityPath) if acpErr != nil { - fmt.Fprintf(os.Stderr, "acp provider: %v\n", acpErr) //nolint:errcheck // best-effort stderr - os.Exit(1) + if requireACPWrapper { + return nil, fmt.Errorf("acp provider: %w", acpErr) + } + return sp, nil } autoSP := sessionauto.New(sp, acpSP) - for _, sessName := range configuredACPSessionNames(sessionBeads, ctx.cityName, ctx.sessionTemplate, ctx.agents) { + for _, sessName := range configuredACPRouteNames(sessionBeads, ctx.cityName, ctx.cfg) { autoSP.RouteACP(sessName) } - return autoSP + return autoSP, nil } - return sp + return sp, nil } -// hasACPAgents reports whether any agent in the config uses session = "acp". -func hasACPAgents(agents []config.Agent) bool { - for _, a := range agents { - if a.Session == "acp" { - return true - } +func agentSessionCreateTransport(cfg *config.City, agentCfg config.Agent) string { + if cfg == nil { + return strings.TrimSpace(agentCfg.Session) + } + resolved, err := config.ResolveProvider( + &agentCfg, + &cfg.Workspace, + cfg.Providers, + func(name string) (string, error) { return name, nil }, + ) + if err != nil { + return strings.TrimSpace(agentCfg.Session) } - return false + return config.ResolveSessionCreateTransport(agentCfg.Session, resolved) } // configuredACPSessionNames resolves the runtime session names for ACP-backed // agents using a single session-bead snapshot. When the snapshot is unavailable // or bead lookup fails, it falls back to the legacy deterministic name. -func configuredACPSessionNames(snapshot *sessionBeadSnapshot, cityName, sessionTemplate string, agents []config.Agent) []string { +func configuredACPSessionNames(snapshot *sessionBeadSnapshot, cityName, sessionTemplate string, cfg *config.City, agents []config.Agent) []string { names := make([]string, 0, len(agents)) for _, a := range agents { - if a.Session != "acp" { + if agentSessionCreateTransport(cfg, a) != "acp" { continue } sessName := agent.SessionNameFor(cityName, a.QualifiedName(), sessionTemplate) @@ -230,6 +251,171 @@ func configuredACPSessionNames(snapshot *sessionBeadSnapshot, cityName, sessionT return names } +func needsACPProviderWrapper(snapshot *sessionBeadSnapshot, cityName string, cfg *config.City) bool { + return requiresACPProviderWrapper(snapshot, cityName, cfg) || (cfg != nil && hasACPProviderTargets(cfg)) +} + +func requiresACPProviderWrapper(snapshot *sessionBeadSnapshot, cityName string, cfg *config.City) bool { + return len(configuredACPRouteNames(snapshot, cityName, cfg)) > 0 +} + +func hasACPProviderTargets(cfg *config.City) bool { + if cfg == nil { + return false + } + candidates := map[string]bool{} + add := func(name string) { + name = strings.TrimSpace(name) + if name != "" { + candidates[name] = true + } + } + add(cfg.Workspace.Provider) + for name := range cfg.Providers { + add(name) + } + for _, agentCfg := range cfg.Agents { + add(agentCfg.Provider) + } + for name := range candidates { + if providerSessionCreateUsesACP(cfg, name) { + return true + } + } + return false +} + +func resolveProviderForACPTransport(cfg *config.City, providerName string) *config.ResolvedProvider { + if cfg == nil || strings.TrimSpace(providerName) == "" { + return nil + } + resolved, err := config.ResolveProvider( + &config.Agent{Provider: providerName}, + &cfg.Workspace, + cfg.Providers, + func(name string) (string, error) { return name, nil }, + ) + if err != nil { + return nil + } + return resolved +} + +func providerSessionCreateUsesACP(cfg *config.City, providerName string) bool { + resolved := resolveProviderForACPTransport(cfg, providerName) + return resolved != nil && resolved.ProviderSessionCreateTransport() == "acp" +} + +func providerLegacyDefaultsToACP(cfg *config.City, providerName string) bool { + resolved := resolveProviderForACPTransport(cfg, providerName) + return resolved != nil && resolved.ProviderSessionCreateTransport() == "acp" +} + +func observedACPSessionNames(snapshot *sessionBeadSnapshot, cfg *config.City) []string { + if snapshot == nil { + return nil + } + names := make([]string, 0, len(snapshot.open)) + seen := make(map[string]bool, len(snapshot.open)) + for _, bead := range snapshot.Open() { + if !beadUsesACPTransport(bead, cfg) { + continue + } + sessionName := strings.TrimSpace(bead.Metadata["session_name"]) + if sessionName == "" || seen[sessionName] { + continue + } + seen[sessionName] = true + names = append(names, sessionName) + } + return names +} + +func beadUsesACPTransport(bead beads.Bead, cfg *config.City) bool { + transport := strings.TrimSpace(bead.Metadata["transport"]) + if transport != "" { + return transport == "acp" + } + providerName := strings.TrimSpace(bead.Metadata["provider"]) + if providerName == "acp" { + return true + } + if strings.TrimSpace(bead.Metadata[session.MCPIdentityMetadataKey]) != "" || + strings.TrimSpace(bead.Metadata[session.MCPServersSnapshotMetadataKey]) != "" { + return true + } + templateName := strings.TrimSpace(bead.Metadata["template"]) + if cfg != nil { + if agentCfg, ok := resolveAgentIdentity(cfg, templateName, currentRigContext(cfg)); ok { + if strings.TrimSpace(agentCfg.Session) != "" && agentSessionCreateTransport(cfg, agentCfg) == "acp" { + return true + } + if strings.TrimSpace(bead.Metadata["command"]) == "" && + strings.TrimSpace(bead.Metadata["pending_create_claim"]) == "true" && + agentSessionCreateTransport(cfg, agentCfg) == "acp" { + return true + } + if providerName == "" { + providerName = strings.TrimSpace(agentCfg.Provider) + } + } + if providerName == "" { + providerName = templateName + } + resolved := resolveProviderForACPTransport(cfg, providerName) + if resolved != nil { + acpCommand := strings.TrimSpace(resolved.ACPCommandString()) + defaultCommand := strings.TrimSpace(resolved.CommandString()) + storedCommand := strings.TrimSpace(bead.Metadata["command"]) + if acpCommand != "" && acpCommand != defaultCommand && + (storedCommand == acpCommand || strings.HasPrefix(storedCommand, acpCommand+" ")) { + return true + } + } + if strings.TrimSpace(bead.Metadata["command"]) == "" && + strings.TrimSpace(bead.Metadata["pending_create_claim"]) == "true" { + return providerLegacyDefaultsToACP(cfg, providerName) + } + } + return false +} + +func configuredACPRouteNames(snapshot *sessionBeadSnapshot, cityName string, cfg *config.City) []string { + names := observedACPSessionNames(snapshot, cfg) + seen := make(map[string]bool, len(names)) + for _, name := range names { + seen[name] = true + } + if cfg == nil { + return names + } + for _, name := range configuredACPSessionNames(snapshot, cityName, cfg.Workspace.SessionTemplate, cfg, cfg.Agents) { + if name == "" || seen[name] { + continue + } + seen[name] = true + names = append(names, name) + } + for _, named := range cfg.NamedSessions { + agentCfg := config.FindAgent(cfg, named.TemplateQualifiedName()) + if agentCfg == nil || agentSessionCreateTransport(cfg, *agentCfg) != "acp" { + continue + } + sessionName := config.NamedSessionRuntimeName(cityName, cfg.Workspace, named.QualifiedName()) + if snapshot != nil { + if snapName := snapshot.FindSessionNameByNamedIdentity(named.QualifiedName()); snapName != "" { + sessionName = snapName + } + } + if sessionName == "" || seen[sessionName] { + continue + } + seen[sessionName] = true + names = append(names, sessionName) + } + return names +} + // displayProviderName returns a human-readable provider name for logging. func displayProviderName(name string) string { if name == "" { diff --git a/cmd/gc/providers_test.go b/cmd/gc/providers_test.go index 6dcf126d4d..e4dd0d17ff 100644 --- a/cmd/gc/providers_test.go +++ b/cmd/gc/providers_test.go @@ -1,6 +1,7 @@ package main import ( + "errors" "os" "path/filepath" "strings" @@ -9,6 +10,7 @@ import ( "github.com/gastownhall/gascity/internal/agent" "github.com/gastownhall/gascity/internal/beads" "github.com/gastownhall/gascity/internal/config" + "github.com/gastownhall/gascity/internal/runtime" ) func TestTmuxConfigFromSessionDefaultsSocketToCityName(t *testing.T) { @@ -225,7 +227,7 @@ func TestConfiguredACPSessionNames_UsesProvidedSnapshot(t *testing.T) { {Name: "mayor"}, } - got := configuredACPSessionNames(snapshot, "city", "", agents) + got := configuredACPSessionNames(snapshot, "city", "", nil, agents) want := []string{ "custom-reviewer", agent.SessionNameFor("city", "witness", ""), @@ -240,6 +242,154 @@ func TestConfiguredACPSessionNames_UsesProvidedSnapshot(t *testing.T) { } } +func TestSessionBeadSnapshotFindSessionNameByNamedIdentity(t *testing.T) { + snapshot := newSessionBeadSnapshot([]beads.Bead{{ + Type: sessionBeadType, + Labels: []string{sessionBeadLabel}, + Metadata: map[string]string{ + "template": "reviewer-template", + "configured_named_identity": "reviewer", + "session_name": "custom-reviewer", + }, + }}) + + if got := snapshot.FindSessionNameByNamedIdentity("reviewer"); got != "custom-reviewer" { + t.Fatalf("FindSessionNameByNamedIdentity(reviewer) = %q, want %q", got, "custom-reviewer") + } +} + +func TestConfiguredACPRouteNames_IncludeNamedSessionRuntimeNames(t *testing.T) { + cfg := &config.City{ + Workspace: config.Workspace{ + Name: "test-city", + }, + Agents: []config.Agent{ + {Name: "reviewer-template", Session: "acp"}, + {Name: "mayor"}, + }, + NamedSessions: []config.NamedSession{ + {Name: "reviewer", Template: "reviewer-template"}, + }, + } + + t.Run("deterministic fallback", func(t *testing.T) { + got := configuredACPRouteNames(nil, "test-city", cfg) + want := []string{ + agent.SessionNameFor("test-city", "reviewer-template", ""), + config.NamedSessionRuntimeName("test-city", cfg.Workspace, "reviewer"), + } + if len(got) != len(want) { + t.Fatalf("configuredACPRouteNames len = %d, want %d (%v)", len(got), len(want), got) + } + for i := range want { + if got[i] != want[i] { + t.Fatalf("configuredACPRouteNames[%d] = %q, want %q", i, got[i], want[i]) + } + } + }) + + t.Run("snapshot override", func(t *testing.T) { + snapshot := newSessionBeadSnapshot([]beads.Bead{{ + Type: sessionBeadType, + Labels: []string{sessionBeadLabel}, + Metadata: map[string]string{ + "template": "reviewer-template", + "configured_named_identity": "reviewer", + "session_name": "custom-reviewer", + }, + }}) + + got := configuredACPRouteNames(snapshot, "test-city", cfg) + want := []string{"custom-reviewer"} + if len(got) != len(want) { + t.Fatalf("configuredACPRouteNames len = %d, want %d (%v)", len(got), len(want), got) + } + for i := range want { + if got[i] != want[i] { + t.Fatalf("configuredACPRouteNames[%d] = %q, want %q", i, got[i], want[i]) + } + } + }) +} + +func TestConfiguredACPRouteNames_IncludeObservedACPProviderSessions(t *testing.T) { + snapshot := newSessionBeadSnapshot([]beads.Bead{{ + Type: sessionBeadType, + Labels: []string{sessionBeadLabel}, + Metadata: map[string]string{ + "template": "opencode", + "provider": "opencode", + "transport": "acp", + "session_name": "provider-session", + }, + }}) + + got := configuredACPRouteNames(snapshot, "test-city", nil) + if len(got) != 1 || got[0] != "provider-session" { + t.Fatalf("configuredACPRouteNames() = %v, want [provider-session]", got) + } +} + +func TestConfiguredACPRouteNames_IncludeLegacyObservedACPProviderSessionsWithoutTransportMetadata(t *testing.T) { + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Providers: map[string]config.ProviderSpec{ + "opencode": { + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: boolPtr(true), + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + snapshot := newSessionBeadSnapshot([]beads.Bead{{ + Type: sessionBeadType, + Labels: []string{sessionBeadLabel}, + Metadata: map[string]string{ + "template": "opencode", + "provider": "opencode", + "command": "/bin/echo acp", + "session_name": "provider-session", + }, + }}) + + got := configuredACPRouteNames(snapshot, "test-city", cfg) + if len(got) != 1 || got[0] != "provider-session" { + t.Fatalf("configuredACPRouteNames() = %v, want [provider-session]", got) + } +} + +func TestConfiguredACPRouteNames_IncludeLegacyObservedCustomACPProviderSessionsWithoutTransportMetadata(t *testing.T) { + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Providers: map[string]config.ProviderSpec{ + "custom-acp": { + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: boolPtr(true), + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + snapshot := newSessionBeadSnapshot([]beads.Bead{{ + Type: sessionBeadType, + Labels: []string{sessionBeadLabel}, + Metadata: map[string]string{ + "template": "custom-acp", + "provider": "custom-acp", + "command": "/bin/echo acp", + "session_name": "provider-session", + }, + }}) + + got := configuredACPRouteNames(snapshot, "test-city", cfg) + if len(got) != 1 || got[0] != "provider-session" { + t.Fatalf("configuredACPRouteNames() = %v, want [provider-session]", got) + } +} + func TestNewSessionProvider_PreregistersACPBeadAndLegacyNames(t *testing.T) { t.Setenv("GC_BEADS", "file") t.Setenv("GC_SESSION", "fake") @@ -281,7 +431,246 @@ func TestNewSessionProvider_PreregistersACPBeadAndLegacyNames(t *testing.T) { } } -func TestLoadProviderSessionSnapshotSkipsStoreWithoutACPAgents(t *testing.T) { +func TestNewSessionProvider_PreregistersACPNamedSessionRuntimeName(t *testing.T) { + t.Setenv("GC_BEADS", "file") + t.Setenv("GC_SESSION", "fake") + + cityDir := t.TempDir() + t.Setenv("GC_CITY", cityDir) + writeACPNamedSessionRouteCityTOML(t, cityDir, "test-city") + + sp := newSessionProvider() + namedRuntime := config.NamedSessionRuntimeName("test-city", config.Workspace{}, "reviewer") + if err := sp.Attach(namedRuntime); err == nil || !strings.Contains(err.Error(), "ACP transport") { + t.Fatalf("Attach(%q) error = %v, want ACP transport error", namedRuntime, err) + } +} + +func TestNewSessionProvider_PreregistersProviderDefaultACPNamedSessionRuntimeName(t *testing.T) { + t.Setenv("GC_BEADS", "file") + t.Setenv("GC_SESSION", "fake") + + cityDir := t.TempDir() + t.Setenv("GC_CITY", cityDir) + writeProviderDefaultACPNamedSessionRouteCityTOML(t, cityDir, "test-city") + + sp := newSessionProvider() + namedRuntime := config.NamedSessionRuntimeName("test-city", config.Workspace{}, "reviewer") + if err := sp.Attach(namedRuntime); err == nil || !strings.Contains(err.Error(), "ACP transport") { + t.Fatalf("Attach(%q) error = %v, want ACP transport error", namedRuntime, err) + } +} + +func TestNewSessionProviderWrapsACPProvidersWithoutACPAgents(t *testing.T) { + ctx := sessionProviderContextForCity(&config.City{ + Workspace: config.Workspace{ + Name: "test-city", + Provider: "opencode", + }, + Providers: map[string]config.ProviderSpec{ + "opencode": { + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: boolPtr(true), + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + }, t.TempDir(), "fake") + + sp := newSessionProviderFromContext(ctx, nil) + if _, ok := sp.(interface{ RouteACP(string) }); !ok { + t.Fatalf("provider = %T, want ACP-routing wrapper", sp) + } +} + +func TestNewSessionProviderWrapsCustomACPProvidersWithExplicitACPConfig(t *testing.T) { + ctx := sessionProviderContextForCity(&config.City{ + Workspace: config.Workspace{ + Name: "test-city", + Provider: "custom-acp", + }, + Providers: map[string]config.ProviderSpec{ + "custom-acp": { + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: boolPtr(true), + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + }, t.TempDir(), "fake") + + sp := newSessionProviderFromContext(ctx, nil) + if _, ok := sp.(interface{ RouteACP(string) }); !ok { + t.Fatalf("provider = %T, want ACP-routing wrapper", sp) + } +} + +func TestNewSessionProviderIgnoresACPInitFailureForUnusedACPProviders(t *testing.T) { + oldBuild := buildSessionProviderByName + t.Cleanup(func() { buildSessionProviderByName = oldBuild }) + buildSessionProviderByName = func(name string, sc config.SessionConfig, cityName, cityPath string) (runtime.Provider, error) { + if name == "acp" { + return nil, errors.New("acp unavailable") + } + return oldBuild(name, sc, cityName, cityPath) + } + + ctx := sessionProviderContextForCity(&config.City{ + Workspace: config.Workspace{ + Name: "test-city", + Provider: "opencode", + }, + Providers: map[string]config.ProviderSpec{ + "opencode": { + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: boolPtr(true), + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + }, t.TempDir(), "fake") + + sp, err := newSessionProviderFromContextWithError(ctx, nil) + if err != nil { + t.Fatalf("newSessionProviderFromContextWithError: %v", err) + } + if _, ok := sp.(interface{ RouteACP(string) }); ok { + t.Fatalf("provider = %T, want plain provider fallback when ACP is unavailable", sp) + } +} + +func TestNewSessionProviderRequiresACPInitForACPAgents(t *testing.T) { + oldBuild := buildSessionProviderByName + t.Cleanup(func() { buildSessionProviderByName = oldBuild }) + buildSessionProviderByName = func(name string, sc config.SessionConfig, cityName, cityPath string) (runtime.Provider, error) { + if name == "acp" { + return nil, errors.New("acp unavailable") + } + return oldBuild(name, sc, cityName, cityPath) + } + + ctx := sessionProviderContextForCity(&config.City{ + Workspace: config.Workspace{ + Name: "test-city", + }, + Agents: []config.Agent{ + {Name: "worker", Provider: "opencode", Session: "acp"}, + }, + Providers: map[string]config.ProviderSpec{ + "opencode": { + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: boolPtr(true), + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + }, t.TempDir(), "fake") + + if _, err := newSessionProviderFromContextWithError(ctx, nil); err == nil { + t.Fatal("newSessionProviderFromContextWithError() error = nil, want ACP init failure") + } +} + +func TestNewSessionProviderRequiresACPInitForImplicitACPTemplates(t *testing.T) { + oldBuild := buildSessionProviderByName + t.Cleanup(func() { buildSessionProviderByName = oldBuild }) + buildSessionProviderByName = func(name string, sc config.SessionConfig, cityName, cityPath string) (runtime.Provider, error) { + if name == "acp" { + return nil, errors.New("acp unavailable") + } + return oldBuild(name, sc, cityName, cityPath) + } + + ctx := sessionProviderContextForCity(&config.City{ + Workspace: config.Workspace{ + Name: "test-city", + }, + Agents: []config.Agent{ + {Name: "worker", Provider: "custom-acp"}, + }, + Providers: map[string]config.ProviderSpec{ + "custom-acp": { + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: boolPtr(true), + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + }, t.TempDir(), "fake") + + if _, err := newSessionProviderFromContextWithError(ctx, nil); err == nil { + t.Fatal("newSessionProviderFromContextWithError() error = nil, want ACP init failure") + } +} + +func TestNewSessionProviderRoutesObservedACPProviderSessionsWithoutACPAgents(t *testing.T) { + t.Setenv("GC_BEADS", "file") + t.Setenv("GC_SESSION", "fake") + + cityDir := t.TempDir() + t.Setenv("GC_CITY", cityDir) + writeACPProviderRouteCityTOML(t, cityDir, "test-city") + + store, err := openCityStoreAt(cityDir) + if err != nil { + t.Fatalf("openCityStoreAt: %v", err) + } + if _, err := store.Create(beads.Bead{ + Type: sessionBeadType, + Labels: []string{sessionBeadLabel}, + Metadata: map[string]string{ + "template": "opencode", + "provider": "opencode", + "transport": "acp", + "session_name": "provider-session", + }, + }); err != nil { + t.Fatalf("Create(provider session bead): %v", err) + } + + sp := newSessionProvider() + if err := sp.Attach("provider-session"); err == nil || !strings.Contains(err.Error(), "ACP transport") { + t.Fatalf("Attach(provider-session) error = %v, want ACP transport error", err) + } +} + +func TestNewSessionProviderRoutesLegacyObservedACPProviderSessionsWithoutTransportMetadata(t *testing.T) { + t.Setenv("GC_BEADS", "file") + t.Setenv("GC_SESSION", "fake") + + cityDir := t.TempDir() + t.Setenv("GC_CITY", cityDir) + writeACPProviderRouteCityTOML(t, cityDir, "test-city") + + store, err := openCityStoreAt(cityDir) + if err != nil { + t.Fatalf("openCityStoreAt: %v", err) + } + if _, err := store.Create(beads.Bead{ + Type: sessionBeadType, + Labels: []string{sessionBeadLabel}, + Metadata: map[string]string{ + "template": "opencode", + "provider": "opencode", + "command": "/bin/echo acp", + "session_name": "provider-session", + }, + }); err != nil { + t.Fatalf("Create(provider session bead): %v", err) + } + + sp := newSessionProvider() + if err := sp.Attach("provider-session"); err == nil || !strings.Contains(err.Error(), "ACP transport") { + t.Fatalf("Attach(provider-session) error = %v, want ACP transport error", err) + } +} + +func TestLoadProviderSessionSnapshotLoadsStoreWithoutACPAgents(t *testing.T) { oldOpen := openSessionProviderStore t.Cleanup(func() { openSessionProviderStore = oldOpen }) @@ -298,11 +687,11 @@ func TestLoadProviderSessionSnapshotSkipsStoreWithoutACPAgents(t *testing.T) { {Name: "mayor"}, }, }) - if snapshot != nil { - t.Fatalf("loadProviderSessionSnapshot() = %#v, want nil", snapshot) + if snapshot == nil { + t.Fatal("loadProviderSessionSnapshot() = nil, want empty snapshot") } - if calls != 0 { - t.Fatalf("openSessionProviderStore called %d times, want 0", calls) + if calls != 1 { + t.Fatalf("openSessionProviderStore called %d times, want 1", calls) } } @@ -379,3 +768,92 @@ start_command = "echo" t.Fatalf("WriteFile(city.toml): %v", err) } } + +func writeACPNamedSessionRouteCityTOML(t *testing.T, dir, cityName string) { + t.Helper() + if err := os.MkdirAll(filepath.Join(dir, ".gc"), 0o755); err != nil { + t.Fatalf("MkdirAll(.gc): %v", err) + } + data := []byte(`[workspace] +name = "` + cityName + `" + +[beads] +provider = "file" + +[[agent]] +name = "reviewer-template" +provider = "claude" +start_command = "echo" +session = "acp" + +[[named_session]] +name = "reviewer" +template = "reviewer-template" + +[[agent]] +name = "mayor" +provider = "claude" +start_command = "echo" +`) + if err := os.WriteFile(filepath.Join(dir, "city.toml"), data, 0o644); err != nil { + t.Fatalf("WriteFile(city.toml): %v", err) + } +} + +func writeProviderDefaultACPNamedSessionRouteCityTOML(t *testing.T, dir, cityName string) { + t.Helper() + if err := os.MkdirAll(filepath.Join(dir, ".gc"), 0o755); err != nil { + t.Fatalf("MkdirAll(.gc): %v", err) + } + data := []byte(`[workspace] +name = "` + cityName + `" + +[beads] +provider = "file" + +[[agent]] +name = "reviewer" +provider = "custom-acp" + +[[named_session]] +template = "reviewer" + +[providers.custom-acp] +command = "/bin/echo" +path_check = "true" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + if err := os.WriteFile(filepath.Join(dir, "city.toml"), data, 0o644); err != nil { + t.Fatalf("WriteFile(city.toml): %v", err) + } +} + +func writeACPProviderRouteCityTOML(t *testing.T, dir, cityName string) { + t.Helper() + if err := os.MkdirAll(filepath.Join(dir, ".gc"), 0o755); err != nil { + t.Fatalf("MkdirAll(.gc): %v", err) + } + data := []byte(`[workspace] +name = "` + cityName + `" + +[beads] +provider = "file" + +[[agent]] +name = "mayor" +provider = "codex" +start_command = "echo" + +[providers.opencode] +command = "/bin/echo" +path_check = "true" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + if err := os.WriteFile(filepath.Join(dir, "city.toml"), data, 0o644); err != nil { + t.Fatalf("WriteFile(city.toml): %v", err) + } +} diff --git a/cmd/gc/session_bead_snapshot.go b/cmd/gc/session_bead_snapshot.go index 1e787572d7..4906256c6a 100644 --- a/cmd/gc/session_bead_snapshot.go +++ b/cmd/gc/session_bead_snapshot.go @@ -115,3 +115,18 @@ func (s *sessionBeadSnapshot) FindSessionNameByTemplate(template string) string } return s.sessionNameByTemplateHint[template] } + +func (s *sessionBeadSnapshot) FindSessionNameByNamedIdentity(identity string) string { + if s == nil || strings.TrimSpace(identity) == "" { + return "" + } + for _, bead := range s.open { + if strings.TrimSpace(bead.Metadata["configured_named_identity"]) != identity { + continue + } + if sessionName := strings.TrimSpace(bead.Metadata["session_name"]); sessionName != "" { + return sessionName + } + } + return "" +} diff --git a/cmd/gc/session_lifecycle_parallel.go b/cmd/gc/session_lifecycle_parallel.go index 923d999d20..6ae7405544 100644 --- a/cmd/gc/session_lifecycle_parallel.go +++ b/cmd/gc/session_lifecycle_parallel.go @@ -690,6 +690,32 @@ func commitStartResultTraced( ClearPendingCreateClaim: shouldRollbackPendingCreate(session), Now: clk.Now(), }) + storedMCPSnapshot, err := sessionpkg.EncodeMCPServersSnapshot(result.prepared.cfg.MCPServers) + if err != nil { + fmt.Fprintf(stderr, "session reconciler: encoding MCP snapshot for %s: %v\n", name, err) //nolint:errcheck + logLifecycleOutcome(stderr, "start", wave, name, tp.TemplateName, "metadata_encode_failed", result.started, result.finished, err) + return false + } + if storedMCPSnapshot != "" || session.Metadata[sessionpkg.MCPServersSnapshotMetadataKey] != "" { + metadata[sessionpkg.MCPServersSnapshotMetadataKey] = storedMCPSnapshot + } + if err := sessionpkg.PersistRuntimeMCPServersSnapshot(result.prepared.cfg.Env["GC_CITY_PATH"], session.ID, result.prepared.cfg.MCPServers); err != nil { + fmt.Fprintf(stderr, "session reconciler: storing runtime MCP snapshot for %s: %v\n", name, err) //nolint:errcheck + logLifecycleOutcome(stderr, "start", wave, name, tp.TemplateName, "runtime_mcp_snapshot_failed", result.started, result.finished, err) + return false + } + if result.prepared.candidate.tp.IsACP || + session.Metadata[sessionpkg.MCPIdentityMetadataKey] != "" || + session.Metadata[sessionpkg.MCPServersSnapshotMetadataKey] != "" { + storedMCPIdentity := firstNonEmptyGCString( + session.Metadata[sessionpkg.MCPIdentityMetadataKey], + session.Metadata[sessionpkg.NamedSessionIdentityMetadata], + session.Metadata["agent_name"], + ) + if storedMCPIdentity != "" || session.Metadata[sessionpkg.MCPIdentityMetadataKey] != "" { + metadata[sessionpkg.MCPIdentityMetadataKey] = storedMCPIdentity + } + } if err := store.SetMetadataBatch(session.ID, metadata); err != nil { fmt.Fprintf(stderr, "session reconciler: storing hashes for %s: %v\n", name, err) //nolint:errcheck if trace != nil { diff --git a/cmd/gc/session_lifecycle_parallel_test.go b/cmd/gc/session_lifecycle_parallel_test.go index 73b4cfef40..1c8eedeeb0 100644 --- a/cmd/gc/session_lifecycle_parallel_test.go +++ b/cmd/gc/session_lifecycle_parallel_test.go @@ -541,6 +541,7 @@ func TestPrepareStartCandidate_UsesSessionIDForTaskWorkDir(t *testing.T) { } func TestExecutePlannedStarts_FreshWakeAfterDrainRetainsStartupContext(t *testing.T) { + skipSlowCmdGCTest(t, "waits through stale session-key detection; run make test-cmd-gc-process for full coverage") sp := runtime.NewFake() store := beads.NewMemStore() clk := &clock.Fake{Time: time.Date(2026, 4, 7, 12, 0, 0, 0, time.UTC)} @@ -2134,6 +2135,7 @@ func (p *dieAfterStartProvider) IsRunning(name string) bool { } func TestExecutePreparedStartWave_StaleSessionKeyDetected(t *testing.T) { + skipSlowCmdGCTest(t, "waits through stale session-key detection; run make test-cmd-gc-process for full coverage") sp := &dieAfterStartProvider{Fake: runtime.NewFake()} item := preparedStart{ candidate: startCandidate{ @@ -2590,3 +2592,61 @@ func TestCommitStartResult_TransitionsCreatingToActive(t *testing.T) { t.Errorf("started_config_hash = %q, want %q", got.Metadata["started_config_hash"], "core-abc") } } + +func TestCommitStartResult_PersistsMCPIdentityForACPStart(t *testing.T) { + store := beads.NewMemStore() + session, err := store.Create(beads.Bead{ + Title: "worker-session", + Type: sessionBeadType, + Labels: []string{sessionBeadLabel}, + Metadata: map[string]string{ + "template": "worker", + "agent_name": "myrig/worker-adhoc-123", + "session_name": "worker-1", + "state": "creating", + }, + }) + if err != nil { + t.Fatal(err) + } + candidate := startCandidate{ + session: &session, + tp: TemplateParams{ + TemplateName: "worker", + InstanceName: "worker-1", + IsACP: true, + }, + } + result := startResult{ + prepared: preparedStart{ + candidate: candidate, + cfg: runtime.Config{ + MCPServers: []runtime.MCPServerConfig{{ + Name: "filesystem", + Transport: runtime.MCPTransportStdio, + Command: "/bin/mcp", + }}, + }, + coreHash: "core-abc", + liveHash: "live-xyz", + }, + outcome: "success", + started: time.Unix(100, 0), + finished: time.Unix(101, 0), + } + rec := events.NewFake() + ok := commitStartResult(result, store, &clock.Fake{Time: time.Unix(102, 0)}, rec, 0, ioDiscard{}, ioDiscard{}) + if !ok { + t.Fatal("commitStartResult returned false for successful start") + } + got, err := store.Get(session.ID) + if err != nil { + t.Fatal(err) + } + if got.Metadata[sessionpkg.MCPIdentityMetadataKey] != "myrig/worker-adhoc-123" { + t.Fatalf("mcp_identity = %q, want %q", got.Metadata[sessionpkg.MCPIdentityMetadataKey], "myrig/worker-adhoc-123") + } + if got.Metadata[sessionpkg.MCPServersSnapshotMetadataKey] == "" { + t.Fatal("mcp_servers_snapshot = empty, want persisted snapshot") + } +} diff --git a/cmd/gc/session_manager_test.go b/cmd/gc/session_manager_test.go index 5624d9b721..d4f86a2819 100644 --- a/cmd/gc/session_manager_test.go +++ b/cmd/gc/session_manager_test.go @@ -1,6 +1,8 @@ package main import ( + "strings" + "github.com/gastownhall/gascity/internal/beads" "github.com/gastownhall/gascity/internal/config" "github.com/gastownhall/gascity/internal/runtime" @@ -12,11 +14,36 @@ func newSessionManagerWithConfig(cityPath string, store beads.Store, sp runtime. return session.NewManagerWithCityPath(store, sp, cityPath) } rigContext := currentRigContext(cfg) - return session.NewManagerWithTransportResolverAndCityPath(store, sp, cityPath, func(template string) string { + return session.NewManagerWithTransportPolicyResolverAndCityPath(store, sp, cityPath, func(template, provider string) (string, bool) { agentCfg, ok := resolveAgentIdentity(cfg, template, rigContext) - if !ok { - return "" + if ok { + resolved, err := config.ResolveProvider( + &agentCfg, + &cfg.Workspace, + cfg.Providers, + func(name string) (string, error) { return name, nil }, + ) + if err != nil { + return agentCfg.Session, strings.TrimSpace(agentCfg.Session) != "" + } + return config.ResolveSessionCreateTransport(agentCfg.Session, resolved), strings.TrimSpace(agentCfg.Session) != "" + } + provider = strings.TrimSpace(provider) + if provider == "" { + provider = strings.TrimSpace(template) + } + if provider == "" { + return "", false + } + resolved, err := config.ResolveProvider( + &config.Agent{Provider: provider}, + &cfg.Workspace, + cfg.Providers, + func(name string) (string, error) { return name, nil }, + ) + if err != nil { + return "", false } - return agentCfg.Session + return strings.TrimSpace(resolved.ProviderSessionCreateTransport()), false }) } diff --git a/cmd/gc/session_template_start.go b/cmd/gc/session_template_start.go index 44f5c3631c..98276d30c4 100644 --- a/cmd/gc/session_template_start.go +++ b/cmd/gc/session_template_start.go @@ -119,7 +119,12 @@ func materializeSessionForTemplateWithOptions( if err != nil { return "", err } - sessionCommand, err := resolvedSessionCommand(cityPath, resolved, nil) + sessionTransport := config.ResolveSessionCreateTransport(spec.Agent.Session, resolved) + sp := newSessionProvider() + if err := validateResolvedSessionTransport(resolved, sessionTransport, sp); err != nil { + return "", err + } + sessionCommand, err := resolvedSessionCommand(cityPath, resolved, nil, sessionTransport) if err != nil { return "", err } @@ -129,7 +134,6 @@ func materializeSessionForTemplateWithOptions( return "", err } - sp := newSessionProvider() title := spec.Identity templateIdentity := namedSessionBackingTemplate(spec) extraMeta := map[string]string{ @@ -169,7 +173,7 @@ func materializeSessionForTemplateWithOptions( sessionCommand, providerName, workDir, - spec.Agent.Session, + sessionTransport, resolved, extraMeta, ) @@ -272,7 +276,12 @@ func materializeSessionForAgentConfig(cityPath string, cfg *config.City, store b if err != nil { return "", err } - sessionCommand, err := resolvedSessionCommand(cityPath, resolved, nil) + sessionTransport := config.ResolveSessionCreateTransport(agentCfg.Session, resolved) + sp := newSessionProvider() + if err := validateResolvedSessionTransport(resolved, sessionTransport, sp); err != nil { + return "", err + } + sessionCommand, err := resolvedSessionCommand(cityPath, resolved, nil, sessionTransport) if err != nil { return "", err } @@ -291,7 +300,6 @@ func materializeSessionForAgentConfig(cityPath string, cfg *config.City, store b return "", err } - sp := newSessionProvider() title := agentCfg.QualifiedName() extraMeta := map[string]string{ "agent_name": sessionQualifiedName, @@ -315,7 +323,7 @@ func materializeSessionForAgentConfig(cityPath string, cfg *config.City, store b sessionCommand, agentCfg.Provider, workDir, - agentCfg.Session, + sessionTransport, resolved, extraMeta, ) diff --git a/cmd/gc/template_resolve.go b/cmd/gc/template_resolve.go index b2eee21319..836d7e0f47 100644 --- a/cmd/gc/template_resolve.go +++ b/cmd/gc/template_resolve.go @@ -103,6 +103,9 @@ type TemplateParams struct { // identity-stamped templates (pool workers, dependency floors) from the // resolver's default stamping on ordinary sessions. EnvIdentityStamped bool + // MCPServers is the effective ACP session/new MCP server set for this + // concrete session context. + MCPServers []runtime.MCPServerConfig } // DisplayName returns the name to use for log messages and event subjects. @@ -127,8 +130,9 @@ func resolveTemplate(p *agentBuildParams, cfgAgent *config.Agent, qualifiedName if err != nil { return TemplateParams{}, fmt.Errorf("agent %q: %w", qualifiedName, err) } + sessionTransport := config.ResolveSessionCreateTransport(cfgAgent.Session, resolved) // Step 2: Validate session vs provider compatibility. - if cfgAgent.Session == "acp" && !resolved.SupportsACP { + if sessionTransport == "acp" && !resolved.SupportsACP { return TemplateParams{}, fmt.Errorf("agent %q: session = \"acp\" but provider %q does not support ACP (set supports_acp = true on the provider)", qualifiedName, resolved.Name) } @@ -147,7 +151,12 @@ func resolveTemplate(p *agentBuildParams, cfgAgent *config.Agent, qualifiedName // Step 5: Build copy_files and command with settings args + schema defaults. var copyFiles []runtime.CopyEntry - command := resolved.CommandString() + var command string + if sessionTransport == "acp" { + command = resolved.ACPCommandString() + } else { + command = resolved.CommandString() + } // Append schema-derived default args (e.g., --dangerously-skip-permissions // from EffectiveDefaults["permission_mode"] = "unrestricted"). if defaultArgs := resolved.ResolveDefaultArgs(); len(defaultArgs) > 0 { @@ -468,6 +477,10 @@ func resolveTemplate(p *agentBuildParams, cfgAgent *config.Agent, qualifiedName ) } } + var mcpServers []runtime.MCPServerConfig + if sessionTransport == "acp" { + mcpServers = materialize.RuntimeMCPServers(mcpCatalog.Servers) + } // Step 12: Build startup hints. hints := agent.StartupHints{ @@ -502,8 +515,9 @@ func resolveTemplate(p *agentBuildParams, cfgAgent *config.Agent, qualifiedName RigName: rigName, RigRoot: rigRoot, WakeMode: cfgAgent.WakeMode, - IsACP: cfgAgent.Session == "acp", + IsACP: sessionTransport == "acp", HookEnabled: hasHooks, + MCPServers: mcpServers, }, nil } @@ -574,10 +588,16 @@ func templateParamsToConfig(tp TemplateParams) runtime.Config { env[startupPromptDeliveredEnv] = "1" } return runtime.Config{ - Command: tp.Command, - PromptSuffix: promptSuffix, - PromptFlag: promptFlag, - Env: env, + Command: tp.Command, + PromptSuffix: promptSuffix, + PromptFlag: promptFlag, + Env: env, + MCPServers: func() []runtime.MCPServerConfig { + if tp.IsACP { + return tp.MCPServers + } + return nil + }(), WorkDir: tp.WorkDir, ReadyPromptPrefix: tp.Hints.ReadyPromptPrefix, ReadyDelayMs: tp.Hints.ReadyDelayMs, diff --git a/cmd/gc/template_resolve_mcp_test.go b/cmd/gc/template_resolve_mcp_test.go index 6f15417a6f..1d3c41ad79 100644 --- a/cmd/gc/template_resolve_mcp_test.go +++ b/cmd/gc/template_resolve_mcp_test.go @@ -90,6 +90,21 @@ args = ["notes-mcp"] } }) + t.Run("non acp runtime excludes mcp servers", func(t *testing.T) { + agent := &config.Agent{Name: "mayor", Scope: "city", Provider: "gemini"} + tp, err := resolveTemplate(buildParams("tmux"), agent, agent.QualifiedName(), nil) + if err != nil { + t.Fatalf("resolveTemplate: %v", err) + } + if len(tp.MCPServers) != 0 { + t.Fatalf("TemplateParams.MCPServers len = %d, want 0", len(tp.MCPServers)) + } + cfg := templateParamsToConfig(tp) + if len(cfg.MCPServers) != 0 { + t.Fatalf("runtime.Config.MCPServers len = %d, want 0", len(cfg.MCPServers)) + } + }) + t.Run("undeliverable runtime hard errors", func(t *testing.T) { agent := &config.Agent{ Name: "worker", diff --git a/cmd/gc/template_resolve_phase2_test.go b/cmd/gc/template_resolve_phase2_test.go index 5eba590d87..a1145425d7 100644 --- a/cmd/gc/template_resolve_phase2_test.go +++ b/cmd/gc/template_resolve_phase2_test.go @@ -67,7 +67,7 @@ func selectedPhase2ProviderCases(t *testing.T) []phase2ProviderCase { { profileID: "codex/tmux-cli", family: "codex", - wantCommand: "codex --dangerously-bypass-approvals-and-sandbox -c model_reasoning_effort=xhigh", + wantCommand: "codex --dangerously-bypass-approvals-and-sandbox --model gpt-5.5 -c model_reasoning_effort=xhigh", wantReadyDelayMs: 3000, wantReadyPromptPrefix: "› ", wantProcessNames: []string{"codex"}, diff --git a/cmd/gc/test_gc_binary_test.go b/cmd/gc/test_gc_binary_test.go index b9fea9965e..c3cac08e29 100644 --- a/cmd/gc/test_gc_binary_test.go +++ b/cmd/gc/test_gc_binary_test.go @@ -24,9 +24,6 @@ func currentGCBinaryForTests(t *testing.T) string { return } binPath := filepath.Join(buildDir, "gc") - goModCache := filepath.Join(buildDir, "gomodcache") - goCache := filepath.Join(buildDir, "gocache") - goPath := filepath.Join(buildDir, "gopath") wd, err := os.Getwd() if err != nil { testGCBinaryErr = fmt.Errorf("getwd: %w", err) @@ -34,11 +31,6 @@ func currentGCBinaryForTests(t *testing.T) string { } cmd := exec.Command("go", "build", "-o", binPath, ".") cmd.Dir = wd - cmd.Env = append(os.Environ(), - "GOMODCACHE="+goModCache, - "GOCACHE="+goCache, - "GOPATH="+goPath, - ) out, err := cmd.CombinedOutput() if err != nil { testGCBinaryErr = fmt.Errorf("go build -o %s .: %w\n%s", binPath, err, string(out)) diff --git a/cmd/gc/worker_handle.go b/cmd/gc/worker_handle.go index 33072d9c2c..400062926a 100644 --- a/cmd/gc/worker_handle.go +++ b/cmd/gc/worker_handle.go @@ -9,6 +9,7 @@ import ( "github.com/gastownhall/gascity/internal/beads" "github.com/gastownhall/gascity/internal/config" + "github.com/gastownhall/gascity/internal/materialize" "github.com/gastownhall/gascity/internal/runtime" "github.com/gastownhall/gascity/internal/session" "github.com/gastownhall/gascity/internal/worker" @@ -24,17 +25,42 @@ func workerSessionCatalogWithConfig(cityPath string, store beads.Store, sp runti func workerFactoryWithConfig(cityPath string, store beads.Store, sp runtime.Provider, cfg *config.City) (*worker.Factory, error) { var ( - resolveTransport func(template string) string + resolveTransport func(template, provider string) string searchPaths []string ) if cfg != nil { rigContext := currentRigContext(cfg) - resolveTransport = func(template string) string { + resolveTransport = func(template, provider string) string { agentCfg, ok := resolveAgentIdentity(cfg, template, rigContext) - if !ok { + if ok { + resolved, err := config.ResolveProvider( + &agentCfg, + &cfg.Workspace, + cfg.Providers, + func(name string) (string, error) { return name, nil }, + ) + if err != nil { + return agentCfg.Session + } + return config.ResolveSessionCreateTransport(agentCfg.Session, resolved) + } + provider = strings.TrimSpace(provider) + if provider == "" { + provider = strings.TrimSpace(template) + } + if provider == "" { return "" } - return agentCfg.Session + resolved, err := config.ResolveProvider( + &config.Agent{Provider: provider}, + &cfg.Workspace, + cfg.Providers, + func(name string) (string, error) { return name, nil }, + ) + if err != nil { + return "" + } + return strings.TrimSpace(resolved.ProviderSessionCreateTransport()) } searchPaths = worker.MergeSearchPaths(cfg.Daemon.ObservePaths) } @@ -52,8 +78,11 @@ func workerSessionRuntimeResolverWithConfig(cityPath string, cfg *config.City) w if cfg == nil { return nil } - return func(info session.Info, sessionKind string) (*worker.ResolvedRuntime, error) { - runtimeCfg := resolvedWorkerRuntimeWithConfig(cityPath, cfg, info, sessionKind) + return func(info session.Info, sessionKind string, metadata map[string]string) (*worker.ResolvedRuntime, error) { + runtimeCfg, err := resolvedWorkerRuntimeWithConfigAndMetadata(cityPath, cfg, info, sessionKind, metadata) + if err != nil { + return nil, err + } if runtimeCfg == nil { return nil, nil } @@ -77,6 +106,93 @@ func workerSessionCreateHints(resolved *config.ResolvedProvider) runtime.Config } } +func resolvedRuntimeMCPServersWithConfig( + cityPath string, + cfg *config.City, + alias, template, provider, workDir string, + transport string, + metadata map[string]string, +) ([]runtime.MCPServerConfig, error) { + if cfg == nil || strings.TrimSpace(workDir) == "" || strings.TrimSpace(transport) != "acp" { + return nil, nil + } + identity := strings.TrimSpace(metadata[session.MCPIdentityMetadataKey]) + if identity == "" { + identity = strings.TrimSpace(metadata["agent_name"]) + } + if identity == "" { + identity = strings.TrimSpace(alias) + } + if identity == "" { + identity = strings.TrimSpace(template) + } + if identity == "" { + identity = strings.TrimSpace(provider) + } + if agentCfg := findAgentByTemplate(cfg, template); agentCfg != nil { + catalog, err := materialize.EffectiveMCPForSession(cfg, cityPath, agentCfg, identity, workDir) + if err != nil { + return nil, fmt.Errorf("loading effective MCP: %w", err) + } + return materialize.RuntimeMCPServers(catalog.Servers), nil + } + synthetic := &config.Agent{Provider: provider} + catalog, err := materialize.EffectiveMCPForSession(cfg, cityPath, synthetic, identity, workDir) + if err != nil { + return nil, fmt.Errorf("loading effective MCP: %w", err) + } + return materialize.RuntimeMCPServers(catalog.Servers), nil +} + +func resumeRuntimeMCPServersWithConfig( + cityPath string, + cfg *config.City, + info session.Info, + resolved *config.ResolvedProvider, + transport string, + metadata map[string]string, +) ([]runtime.MCPServerConfig, error) { + if cfg == nil || resolved == nil { + return nil, nil + } + workDir := strings.TrimSpace(info.WorkDir) + if workDir == "" { + workDir = cityPath + } + resumeMeta := make(map[string]string) + for key, value := range metadata { + resumeMeta[key] = value + } + if agentName := strings.TrimSpace(info.AgentName); agentName != "" { + resumeMeta["agent_name"] = agentName + } + mcpServers, err := resolvedRuntimeMCPServersWithConfig( + cityPath, + cfg, + info.Alias, + info.Template, + firstNonEmptyGCString(info.Provider, resolved.Name, info.Template), + workDir, + transport, + resumeMeta, + ) + if err == nil { + return mcpServers, nil + } + runtimeSnapshot, loadErr := session.LoadRuntimeMCPServersSnapshot(cityPath, info.ID) + if loadErr != nil { + return nil, loadErr + } + if len(runtimeSnapshot) > 0 { + return runtimeSnapshot, nil + } + stored, decodeErr := session.DecodeMCPServersSnapshot(resumeMeta[session.MCPServersSnapshotMetadataKey]) + if decodeErr != nil { + return nil, fmt.Errorf("decoding stored MCP snapshot: %w", decodeErr) + } + return session.SanitizeStoredMCPSnapshotForResume(stored), nil +} + func newWorkerSessionHandleForResolvedRuntimeWithConfig( cityPath string, store beads.Store, @@ -90,6 +206,10 @@ func newWorkerSessionHandleForResolvedRuntimeWithConfig( if err != nil { return nil, err } + mcpServers, err := resolvedRuntimeMCPServersWithConfig(cityPath, cfg, alias, template, provider, workDir, transport, metadata) + if err != nil { + return nil, err + } sessionCfg, err := resolvedWorkerSessionConfigWithConfig( command, provider, @@ -101,6 +221,7 @@ func newWorkerSessionHandleForResolvedRuntimeWithConfig( transport, resolved, metadata, + mcpServers, ) if err != nil { return nil, err @@ -119,13 +240,29 @@ func resolvedWorkerSessionConfigWithConfig( transport string, resolved *config.ResolvedProvider, metadata map[string]string, + mcpServers []runtime.MCPServerConfig, ) (worker.ResolvedSessionConfig, error) { if resolved == nil { return worker.ResolvedSessionConfig{}, fmt.Errorf("resolved provider is required") } + if transport == "acp" { + var err error + metadata, err = session.WithStoredMCPMetadata( + metadata, + firstNonEmptyGCString(metadata[session.MCPIdentityMetadataKey], metadata["agent_name"]), + mcpServers, + ) + if err != nil { + return worker.ResolvedSessionConfig{}, err + } + } command = strings.TrimSpace(command) if command == "" { - command = strings.TrimSpace(resolved.CommandString()) + if transport == "acp" { + command = strings.TrimSpace(resolved.ACPCommandString()) + } else { + command = strings.TrimSpace(resolved.CommandString()) + } } providerName := strings.TrimSpace(resolved.Name) if providerName == "" { @@ -152,7 +289,11 @@ func resolvedWorkerSessionConfigWithConfig( ResumeCommand: resolved.ResumeCommand, SessionIDFlag: resolved.SessionIDFlag, }, - Hints: workerSessionCreateHints(resolved), + Hints: func() runtime.Config { + hints := workerSessionCreateHints(resolved) + hints.MCPServers = mcpServers + return hints + }(), }, }) } @@ -313,29 +454,36 @@ func workerRespondSessionTargetWithConfig(cityPath string, store beads.Store, sp return handle.Respond(context.Background(), response) } -func resolvedWorkerRuntimeWithConfig(cityPath string, cfg *config.City, info session.Info, sessionKind string) *worker.ResolvedRuntime { +func resolvedWorkerRuntimeWithConfig(cityPath string, cfg *config.City, info session.Info, sessionKind string) (*worker.ResolvedRuntime, error) { + return resolvedWorkerRuntimeWithConfigAndMetadata(cityPath, cfg, info, sessionKind, nil) +} + +func resolvedWorkerRuntimeWithConfigAndMetadata(cityPath string, cfg *config.City, info session.Info, sessionKind string, metadata map[string]string) (*worker.ResolvedRuntime, error) { if cfg == nil { - return nil + return nil, nil } - resolved := resolveWorkerRuntimeWithConfig(cfg, info, sessionKind) + resolved, configuredTransport := resolveWorkerRuntimeProviderWithConfig(cfg, info, sessionKind) if resolved == nil { - return nil + return nil, nil } - - command := strings.TrimSpace(info.Command) - if !shouldPreserveStoredRuntimeCommand(command, resolved.CommandString()) { - launchCommand, err := config.BuildProviderLaunchCommand(cityPath, resolved, nil) - command = resolved.CommandString() - if err == nil { - command = launchCommand.Command - } + transport := resolvedWorkerRuntimeTransport(info, resolved, configuredTransport, metadata) + if transport == "" && startedConfigHashProvesWorkerACPTransport(cityPath, cfg, info, sessionKind, resolved, metadata, configuredTransport) { + transport = "acp" + } + if transport == "" && legacyWorkerACPTransportAmbiguous(resolved, configuredTransport, info.Command, metadata) { + return nil, fmt.Errorf("legacy session transport is ambiguous: recreate the stopped session or resume it while ACP metadata can still be persisted") } - command = firstNonEmptyGCString(command, info.Provider, resolved.Name) + + command := resolvedWorkerRuntimeCommandForTransport(cityPath, resolved, transport, info.Command, info.Provider, metadata) workDir := strings.TrimSpace(info.WorkDir) if workDir == "" { workDir = cityPath } + mcpServers, err := resumeRuntimeMCPServersWithConfig(cityPath, cfg, info, resolved, transport, metadata) + if err != nil { + return nil, err + } return &worker.ResolvedRuntime{ Command: command, WorkDir: workDir, @@ -347,6 +495,7 @@ func resolvedWorkerRuntimeWithConfig(cityPath string, cfg *config.City, info ses ReadyDelayMs: resolved.ReadyDelayMs, ProcessNames: resolved.ProcessNames, EmitsPermissionWarning: resolved.EmitsPermissionWarning, + MCPServers: mcpServers, }, Resume: session.ProviderResume{ ResumeFlag: firstNonEmptyGCString(resolved.ResumeFlag, info.ResumeFlag), @@ -354,7 +503,41 @@ func resolvedWorkerRuntimeWithConfig(cityPath string, cfg *config.City, info ses ResumeCommand: firstNonEmptyGCString(resolved.ResumeCommand, info.ResumeCommand), SessionIDFlag: resolved.SessionIDFlag, }, + }, nil +} + +func resolvedWorkerRuntimeCommandForTransport(cityPath string, resolved *config.ResolvedProvider, transport, storedCommand, fallbackProvider string, metadata map[string]string) string { + command := strings.TrimSpace(storedCommand) + configuredCommand := configuredWorkerRuntimeCommand(resolved, transport) + if configuredCommand == "" { + return firstNonEmptyGCString(command, fallbackProvider, resolved.Name) + } + desiredCommand := configuredCommand + if optionOverrides, err := session.ParseTemplateOverrides(metadata); err == nil { + if launchCommand, err := config.BuildProviderLaunchCommand(cityPath, resolved, optionOverrides, transport); err == nil { + desiredCommand = firstNonEmptyGCString(launchCommand.Command, configuredCommand, resolved.Name) + if shouldPreserveStoredRuntimeCommandForTransport(command, desiredCommand, transport, optionOverrides) { + desiredCommand = command + } + } } + if !shouldPreserveStoredRuntimeCommand(command, desiredCommand) { + command = desiredCommand + } + return firstNonEmptyGCString(command, fallbackProvider, resolved.Name) +} + +func configuredWorkerRuntimeCommand(resolved *config.ResolvedProvider, transport string) string { + if resolved == nil { + return "" + } + if transport == "acp" && (strings.TrimSpace(resolved.ACPCommand) != "" || resolved.ACPArgs != nil) { + return strings.TrimSpace(resolved.ACPCommandString()) + } + if strings.TrimSpace(resolved.Command) != "" { + return strings.TrimSpace(resolved.CommandString()) + } + return "" } func shouldPreserveStoredRuntimeCommand(storedCommand, resolvedCommand string) bool { @@ -366,25 +549,165 @@ func shouldPreserveStoredRuntimeCommand(storedCommand, resolvedCommand string) b if resolvedCommand == "" { return true } - return storedCommand == resolvedCommand || strings.HasPrefix(storedCommand, resolvedCommand+" ") + // A bare stored command (just the provider binary) lacks schema + // defaults like --dangerously-skip-permissions and the --settings + // path. Rebuild from the current config instead of preserving it. + // See #799: pool-agent sessions resumed through the control- + // dispatcher path wedged on interactive permission prompts because + // the bare stored command was preserved without re-injecting flags. + if storedCommand == resolvedCommand { + return false + } + return strings.HasPrefix(storedCommand, resolvedCommand+" ") +} + +func shouldPreserveStoredRuntimeCommandForTransport(storedCommand, resolvedCommand, _ string, optionOverrides map[string]string) bool { + if shouldPreserveStoredRuntimeCommand(storedCommand, resolvedCommand) { + return true + } + if len(optionOverrides) == 0 && storedCommandHasSettingsArg(storedCommand) && sameRuntimeCommandExecutable(storedCommand, resolvedCommand) { + return true + } + return false +} + +func sameRuntimeCommandExecutable(storedCommand, resolvedCommand string) bool { + storedFields := strings.Fields(strings.TrimSpace(storedCommand)) + resolvedFields := strings.Fields(strings.TrimSpace(resolvedCommand)) + if len(storedFields) == 0 || len(resolvedFields) == 0 { + return false + } + return storedFields[0] == resolvedFields[0] +} + +func storedCommandHasSettingsArg(command string) bool { + return strings.Contains(" "+strings.TrimSpace(command)+" ", " --settings ") } -func resolveWorkerRuntimeWithConfig(cfg *config.City, info session.Info, sessionKind string) *config.ResolvedProvider { +func storedWorkerSessionProvesACPTransport(resolved *config.ResolvedProvider, configuredTransport, storedCommand string, metadata map[string]string) bool { + if metadata != nil { + if strings.TrimSpace(metadata[session.MCPIdentityMetadataKey]) != "" || + strings.TrimSpace(metadata[session.MCPServersSnapshotMetadataKey]) != "" { + return true + } + if strings.TrimSpace(configuredTransport) == "acp" && legacyWorkerResumeMetadataProvesACPTransport(metadata) { + return true + } + } + if resolved == nil { + return false + } + acpCommand := strings.TrimSpace(resolved.ACPCommandString()) + defaultCommand := strings.TrimSpace(resolved.CommandString()) + if acpCommand == "" || acpCommand == defaultCommand { + return false + } + return shouldPreserveStoredRuntimeCommand(storedCommand, acpCommand) +} + +func legacyWorkerResumeMetadataProvesACPTransport(metadata map[string]string) bool { + if metadata == nil { + return false + } + return strings.TrimSpace(metadata["resume_command"]) != "" || + strings.TrimSpace(metadata["resume_flag"]) != "" || + strings.TrimSpace(metadata["session_key"]) != "" +} + +func legacyWorkerACPTransportAmbiguous(resolved *config.ResolvedProvider, configuredTransport, storedCommand string, metadata map[string]string) bool { + if strings.TrimSpace(configuredTransport) != "acp" || resolved == nil { + return false + } + if storedWorkerSessionProvesACPTransport(resolved, configuredTransport, storedCommand, metadata) { + return false + } + acpCommand := strings.TrimSpace(resolved.ACPCommandString()) + defaultCommand := strings.TrimSpace(resolved.CommandString()) + if acpCommand == "" || acpCommand != defaultCommand { + return false + } + storedCommand = strings.TrimSpace(storedCommand) + return storedCommand == "" || sameRuntimeCommandExecutable(storedCommand, defaultCommand) +} + +func startedConfigHashProvesWorkerACPTransport( + cityPath string, + cfg *config.City, + info session.Info, + _ string, + resolved *config.ResolvedProvider, + metadata map[string]string, + configuredTransport string, +) bool { + if cfg == nil || resolved == nil || metadata == nil || strings.TrimSpace(configuredTransport) != "acp" { + return false + } + startedHash := strings.TrimSpace(metadata["started_config_hash"]) + if startedHash == "" { + return false + } + acpCommand := resolvedWorkerRuntimeCommandForTransport(cityPath, resolved, "acp", info.Command, info.Provider, metadata) + defaultCommand := resolvedWorkerRuntimeCommandForTransport(cityPath, resolved, "", info.Command, info.Provider, metadata) + mcpServers, err := resolvedRuntimeMCPServersWithConfig( + cityPath, + cfg, + info.Alias, + info.Template, + firstNonEmptyGCString(info.Provider, resolved.Name, info.Template), + firstNonEmptyGCString(info.WorkDir, cityPath), + "acp", + metadata, + ) + if err != nil { + return false + } + acpHash := runtime.CoreFingerprint(runtime.Config{ + Command: acpCommand, + Env: resolved.Env, + MCPServers: mcpServers, + }) + defaultHash := runtime.CoreFingerprint(runtime.Config{ + Command: defaultCommand, + Env: resolved.Env, + }) + if acpHash == defaultHash { + return false + } + return startedHash == acpHash +} + +func resolvedWorkerRuntimeTransport(info session.Info, resolved *config.ResolvedProvider, configuredTransport string, metadata map[string]string) string { + if transport := strings.TrimSpace(info.Transport); transport != "" { + return transport + } + if strings.TrimSpace(info.Provider) == "acp" { + return "acp" + } + if storedWorkerSessionProvesACPTransport(resolved, configuredTransport, info.Command, metadata) { + return "acp" + } + if strings.TrimSpace(info.Command) == "" { + return strings.TrimSpace(configuredTransport) + } + return "" +} + +func resolveWorkerRuntimeProviderWithConfig(cfg *config.City, info session.Info, sessionKind string) (*config.ResolvedProvider, string) { if cfg == nil { - return nil + return nil, "" } if sessionKind != "provider" { if found, ok := resolveAgentIdentity(cfg, info.Template, ""); ok { if resolved, err := config.ResolveProvider(&found, &cfg.Workspace, cfg.Providers, exec.LookPath); err == nil { - return resolved + return resolved, config.ResolveSessionCreateTransport(found.Session, resolved) } } } resolved, err := config.ResolveProvider(&config.Agent{Provider: info.Template}, &cfg.Workspace, cfg.Providers, exec.LookPath) if err != nil { - return nil + return nil, "" } - return resolved + return resolved, strings.TrimSpace(resolved.ProviderSessionCreateTransport()) } func workerDeliveryIntentForSubmitIntent(intent session.SubmitIntent) worker.DeliveryIntent { diff --git a/cmd/gc/worker_handle_test.go b/cmd/gc/worker_handle_test.go index d821c06a54..79b916c5e5 100644 --- a/cmd/gc/worker_handle_test.go +++ b/cmd/gc/worker_handle_test.go @@ -29,6 +29,7 @@ func (s *failingSessionLookupStore) List(beads.ListQuery) ([]beads.Bead, error) } func TestWorkerHandleForSessionWithConfigUsesResolvedProviderOnFirstStart(t *testing.T) { + skipSlowCmdGCTest(t, "waits through stale session-key detection; run make test-cmd-gc-process for full coverage") cityDir := t.TempDir() writePhase0InterfaceCity(t, cityDir, `[workspace] name = "test-city" @@ -124,25 +125,588 @@ func TestResolvedWorkerRuntimeWithConfigUsesProviderLaunchCommand(t *testing.T) }, } - resolved := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + resolved, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ Template: "worker", WorkDir: cityDir, }, "") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } if resolved == nil { t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") } if !strings.Contains(resolved.Command, "--dangerously-skip-permissions") { t.Fatalf("Command = %q, want unrestricted default", resolved.Command) } - if !strings.Contains(resolved.Command, "--effort max") { - t.Fatalf("Command = %q, want effort max default", resolved.Command) + if !strings.Contains(resolved.Command, "--effort max") { + t.Fatalf("Command = %q, want effort max default", resolved.Command) + } + if !strings.Contains(resolved.Command, "--settings") { + t.Fatalf("Command = %q, want settings arg", resolved.Command) + } +} + +// TestResolvedWorkerRuntimeResumesPoolSessionPreservesLaunchFlags is a +// regression test for gastownhall/gascity#799: a pool-agent session +// resumed through the control-dispatcher path must reconstruct the full +// launch command (--dangerously-skip-permissions, --settings, schema +// defaults) even when the persisted session command is the bare +// provider name. The pre-fix path dropped those flags and caused pool +// workers resumed via `claude --resume ` to wedge on interactive +// permission prompts. +func TestResolvedWorkerRuntimeResumesPoolSessionPreservesLaunchFlags(t *testing.T) { + cityDir := t.TempDir() + gcDir := filepath.Join(cityDir, ".gc") + if err := os.MkdirAll(gcDir, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(gcDir, "settings.json"), []byte(`{"hooks":{}}`), 0o644); err != nil { + t.Fatal(err) + } + + claude := config.BuiltinProviders()["claude"] + maxActive := 3 + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{{ + Name: "perspective_planner", + Provider: "claude", + MaxActiveSessions: &maxActive, + }}, + Providers: map[string]config.ProviderSpec{ + "claude": claude, + }, + } + + // Simulate a pool-instance session bead whose persisted command is + // the bare provider name — the shape produced before the April 2026 + // worker-boundary refactor when the API created the bead with + // sessionCreateAgentCommand(resolved) before the reconciler synced + // the full tp.Command. + runtimeCfg, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + Template: "perspective_planner", + Command: "claude", + WorkDir: cityDir, + }, "") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") + } + if !strings.Contains(runtimeCfg.Command, "--dangerously-skip-permissions") { + t.Fatalf("resumed pool Command = %q, want --dangerously-skip-permissions", runtimeCfg.Command) + } + if !strings.Contains(runtimeCfg.Command, "--effort max") { + t.Fatalf("resumed pool Command = %q, want --effort max default", runtimeCfg.Command) + } + if !strings.Contains(runtimeCfg.Command, "--settings") { + t.Fatalf("resumed pool Command = %q, want --settings arg", runtimeCfg.Command) + } +} + +func TestShouldPreserveStoredRuntimeCommandForTransportRejectsExecutableOnlyMatch(t *testing.T) { + if shouldPreserveStoredRuntimeCommandForTransport( + "claude", + "claude --settings /tmp/settings.json", + "", + nil, + ) { + t.Fatal("shouldPreserveStoredRuntimeCommandForTransport() = true, want false") + } +} + +func TestResolvedWorkerRuntimeWithConfigUsesStoredTemplateACPTransport(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[agent]] +name = "worker" +provider = "stub" +session = "acp" + +[providers.stub] +command = "/bin/echo" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + writeCatalogFile(t, cityDir, "mcp/filesystem.toml", ` +name = "filesystem" +command = "/bin/mcp" +args = ["--stdio"] + +[env] +TOKEN = "abc" +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + resolved, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + Template: "worker", + Command: "/bin/echo", + Transport: "acp", + WorkDir: cityDir, + }, "") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") + } + if got, want := resolved.Command, "/bin/echo acp"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } + if len(resolved.Hints.MCPServers) != 1 { + t.Fatalf("Hints.MCPServers len = %d, want 1", len(resolved.Hints.MCPServers)) + } + if got, want := resolved.Hints.MCPServers[0].Name, "filesystem"; got != want { + t.Fatalf("Hints.MCPServers[0].Name = %q, want %q", got, want) + } +} + +func TestResolvedWorkerRuntimeWithConfigDoesNotInferConfiguredTransportWithoutStoredTemplateACPMetadata(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[agent]] +name = "worker" +provider = "stub" +session = "acp" + +[providers.stub] +command = "/bin/echo" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + resolved, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + Template: "worker", + Command: "/bin/echo", + WorkDir: cityDir, + }, "") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") + } + if got, want := resolved.Command, "/bin/echo"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } +} + +func TestResolvedWorkerRuntimeTransportUsesResumeMetadataForLegacyACPWithSameCommand(t *testing.T) { + resolved := &config.ResolvedProvider{ + Command: "/bin/echo", + ACPCommand: "/bin/echo", + } + + got := resolvedWorkerRuntimeTransport(session.Info{ + Command: "/bin/echo", + }, resolved, "acp", map[string]string{ + "resume_flag": "--resume", + }) + if got != "acp" { + t.Fatalf("resolvedWorkerRuntimeTransport() = %q, want acp", got) + } +} + +func TestResolvedWorkerRuntimeWithConfigErrorsForAmbiguousLegacyACPTransportWithSameCommand(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[agent]] +name = "worker" +provider = "stub" +session = "acp" + +[providers.stub] +command = "/bin/echo" +supports_acp = true +acp_command = "/bin/echo" +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + _, err = resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + Template: "worker", + Command: "/bin/echo", + WorkDir: cityDir, + }, "") + if err == nil || !strings.Contains(err.Error(), "legacy session transport is ambiguous") { + t.Fatalf("resolvedWorkerRuntimeWithConfig() error = %v, want ambiguous legacy ACP transport", err) + } +} + +func TestResolvedWorkerRuntimeWithConfigUsesStartedConfigHashForLegacyProviderACPWithSameCommand(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[providers.custom-acp] +command = "/bin/echo" +path_check = "true" +supports_acp = true +acp_command = "/bin/echo" +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + cfg.PackMCPDir = filepath.Join(cityDir, "mcp") + if err := os.MkdirAll(cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(cfg.PackMCPDir, "identity.template.toml"), []byte(` +name = "identity" +command = "/bin/mcp" +args = ["{{.AgentName}}"] +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + + info := session.Info{ + Template: "custom-acp", + Command: "/bin/echo", + Provider: "custom-acp", + WorkDir: cityDir, + } + resolved, _ := resolveWorkerRuntimeProviderWithConfig(cfg, info, "provider") + mcpServers, err := resolvedRuntimeMCPServersWithConfig( + cityDir, + cfg, + info.Alias, + info.Template, + info.Provider, + info.WorkDir, + "acp", + nil, + ) + if err != nil { + t.Fatalf("resolvedRuntimeMCPServersWithConfig: %v", err) + } + startedHash := runtime.CoreFingerprint(runtime.Config{ + Command: resolved.ACPCommandString(), + Env: resolved.Env, + MCPServers: mcpServers, + }) + + runtimeCfg, err := resolvedWorkerRuntimeWithConfigAndMetadata(cityDir, cfg, info, "provider", map[string]string{ + "started_config_hash": startedHash, + }) + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfigAndMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolvedWorkerRuntimeWithConfigAndMetadata() = nil") + } + if len(runtimeCfg.Hints.MCPServers) != 1 { + t.Fatalf("len(runtimeCfg.Hints.MCPServers) = %d, want 1", len(runtimeCfg.Hints.MCPServers)) + } +} + +func TestResolvedWorkerRuntimeWithConfigKeepsDefaultTransportWithoutExplicitACPTemplate(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[agent]] +name = "worker" +provider = "stub" + +[providers.stub] +command = "/bin/echo" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + resolved, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + Template: "worker", + Command: "/bin/echo", + WorkDir: cityDir, + }, "") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") + } + if got, want := resolved.Command, "/bin/echo"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } +} + +func TestResolvedWorkerRuntimeWithConfigUsesStoredACPTransportForProviderSession(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[providers.opencode] +command = "/bin/echo" +path_check = "true" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + resolved, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + Template: "opencode", + Command: "/bin/echo", + Transport: "acp", + WorkDir: cityDir, + }, "provider") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") + } + if got, want := resolved.Command, "/bin/echo acp"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } +} + +func TestResolvedWorkerRuntimeWithConfigUsesStoredACPTransportForLegacyProviderSessionWithoutMetadata(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[providers.opencode] +command = "/bin/echo" +path_check = "true" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + resolved, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + Template: "opencode", + Command: "/bin/echo acp", + WorkDir: cityDir, + }, "provider") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") + } + if got, want := resolved.Command, "/bin/echo acp"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } +} + +func TestResolvedWorkerRuntimeWithConfigUsesStoredACPTransportForLegacyProviderSessionOnACPEnabledCustomProvider(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[providers.custom-acp] +command = "/bin/echo" +path_check = "true" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + resolved, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + Template: "custom-acp", + Command: "/bin/echo acp", + WorkDir: cityDir, + }, "provider") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") + } + if got, want := resolved.Command, "/bin/echo acp"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } +} + +func TestResolvedWorkerRuntimeWithConfigUsesProviderACPDefaultForAgentTemplateWithoutSessionOverride(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[agent]] +name = "worker" +dir = "myrig" +provider = "custom-acp" + +[providers.custom-acp] +command = "/bin/echo" +path_check = "true" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + resolved, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + Template: "myrig/worker", + WorkDir: cityDir, + }, "") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") + } + if got, want := resolved.Command, "/bin/echo acp"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } +} + +func TestResolvedWorkerRuntimeWithConfigReplaysTemplateOverridesOnResume(t *testing.T) { + cityDir := t.TempDir() + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{{ + Name: "worker", + Dir: "myrig", + Provider: "custom", + }}, + Providers: map[string]config.ProviderSpec{ + "custom": { + Command: "/bin/echo", + PathCheck: "true", + OptionsSchema: []config.ProviderOption{{ + Key: "effort", + Type: "select", + Choices: []config.OptionChoice{{ + Value: "high", + FlagArgs: []string{"--effort", "high"}, + }}, + }}, + }, + }, + } + + resolved, err := resolvedWorkerRuntimeWithConfigAndMetadata(cityDir, cfg, session.Info{ + Template: "myrig/worker", + Command: "/bin/echo", + WorkDir: cityDir, + }, "", map[string]string{ + "template_overrides": `{"effort":"high","initial_message":"hello"}`, + }) + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfigAndMetadata: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfigAndMetadata() = nil") + } + if got, want := resolved.Command, "/bin/echo --effort high"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } +} + +func TestResolvedWorkerRuntimeWithConfigFallsBackToStoredCommandWhenTemplateOverridesInvalid(t *testing.T) { + cityDir := t.TempDir() + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{{ + Name: "worker", + Dir: "myrig", + Provider: "custom", + }}, + Providers: map[string]config.ProviderSpec{ + "custom": { + Command: "/bin/echo", + PathCheck: "true", + }, + }, + } + + resolved, err := resolvedWorkerRuntimeWithConfigAndMetadata(cityDir, cfg, session.Info{ + Template: "myrig/worker", + Command: "/bin/echo --stored", + WorkDir: cityDir, + }, "", map[string]string{ + "template_overrides": `{`, + }) + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfigAndMetadata: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfigAndMetadata() = nil") } - if !strings.Contains(resolved.Command, "--settings") { - t.Fatalf("Command = %q, want settings arg", resolved.Command) + if got, want := resolved.Command, "/bin/echo --stored"; got != want { + t.Fatalf("Command = %q, want %q", got, want) } } func TestWorkerHandleForSessionWithConfigUsesResolvedProviderOnResume(t *testing.T) { + skipSlowCmdGCTest(t, "waits through stale session-key detection; run make test-cmd-gc-process for full coverage") cityDir := t.TempDir() writePhase0InterfaceCity(t, cityDir, `[workspace] name = "test-city" @@ -421,6 +985,7 @@ func TestResolvedWorkerSessionConfigWithConfigFallsBackToResolvedProviderNameFor Name: "custom-provider", }, map[string]string{"session_origin": "test"}, + nil, ) if err != nil { t.Fatalf("resolvedWorkerSessionConfigWithConfig: %v", err) @@ -445,6 +1010,7 @@ func TestResolvedWorkerSessionConfigWithConfigFallsBackToProviderArgForCommand(t "", &config.ResolvedProvider{}, nil, + nil, ) if err != nil { t.Fatalf("resolvedWorkerSessionConfigWithConfig: %v", err) @@ -457,6 +1023,71 @@ func TestResolvedWorkerSessionConfigWithConfigFallsBackToProviderArgForCommand(t } } +func TestResolvedWorkerSessionConfigWithConfigPersistsStoredMCPMetadata(t *testing.T) { + cfg, err := resolvedWorkerSessionConfigWithConfig( + "", + "legacy-provider", + "/tmp/work", + "worker", + "", + "worker", + "Worker", + "acp", + &config.ResolvedProvider{ + Name: "custom-provider", + }, + map[string]string{ + "session_origin": "test", + "agent_name": "myrig/worker-adhoc-123", + }, + []runtime.MCPServerConfig{{ + Name: "filesystem", + Transport: runtime.MCPTransportStdio, + Command: "/bin/mcp", + Args: []string{"--stdio"}, + }}, + ) + if err != nil { + t.Fatalf("resolvedWorkerSessionConfigWithConfig: %v", err) + } + if got, want := cfg.Metadata[session.MCPIdentityMetadataKey], "myrig/worker-adhoc-123"; got != want { + t.Fatalf("Metadata[mcp_identity] = %q, want %q", got, want) + } + if got := cfg.Metadata[session.MCPServersSnapshotMetadataKey]; got == "" { + t.Fatal("Metadata[mcp_servers_snapshot] = empty, want persisted snapshot") + } +} + +func TestResolvedWorkerSessionConfigWithConfigSkipsStoredMCPMetadataForTmuxTransport(t *testing.T) { + cfg, err := resolvedWorkerSessionConfigWithConfig( + "", + "legacy-provider", + "/tmp/work", + "worker", + "", + "worker", + "Worker", + "", + &config.ResolvedProvider{ + Name: "custom-provider", + }, + map[string]string{ + "session_origin": "test", + "agent_name": "myrig/worker-adhoc-123", + }, + nil, + ) + if err != nil { + t.Fatalf("resolvedWorkerSessionConfigWithConfig: %v", err) + } + if got := cfg.Metadata[session.MCPIdentityMetadataKey]; got != "" { + t.Fatalf("Metadata[mcp_identity] = %q, want empty for tmux transport", got) + } + if got := cfg.Metadata[session.MCPServersSnapshotMetadataKey]; got != "" { + t.Fatalf("Metadata[mcp_servers_snapshot] = %q, want empty for tmux transport", got) + } +} + func TestResolvedWorkerRuntimeWithConfigFallsBackToCityPathAndSyncsHintsWorkDir(t *testing.T) { cityDir := t.TempDir() writePhase0InterfaceCity(t, cityDir, `[workspace] @@ -480,9 +1111,12 @@ ready_delay_ms = 250 t.Fatalf("loadCityConfig: %v", err) } - runtimeCfg := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + runtimeCfg, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ Template: "worker", }, "") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } if runtimeCfg == nil { t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") } @@ -500,6 +1134,395 @@ ready_delay_ms = 250 } } +func TestResolvedWorkerRuntimeWithConfigIgnoresMCPResolutionErrorForACPResume(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[agent]] +name = "worker" +provider = "stub" +session = "acp" + +[providers.stub] +command = "/bin/echo" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + writeCatalogFile(t, cityDir, "mcp/filesystem.toml", ` +name = "filesystem" +command = [broken +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + resolved, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + Template: "worker", + Transport: "acp", + WorkDir: cityDir, + }, "") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") + } + if got, want := resolved.Command, "/bin/echo acp"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } + if len(resolved.Hints.MCPServers) != 0 { + t.Fatalf("Hints.MCPServers len = %d, want 0", len(resolved.Hints.MCPServers)) + } +} + +func TestResolvedWorkerRuntimeWithConfigIgnoresMCPResolutionErrorWithoutACPTransport(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[agent]] +name = "worker" +provider = "stub" + +[providers.stub] +command = "/bin/echo" +`) + writeCatalogFile(t, cityDir, "mcp/filesystem.toml", ` +name = "filesystem" +command = [broken +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + resolved, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + Template: "worker", + WorkDir: cityDir, + }, "") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") + } + if got, want := resolved.Command, "/bin/echo"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } + if len(resolved.Hints.MCPServers) != 0 { + t.Fatalf("Hints.MCPServers len = %d, want 0", len(resolved.Hints.MCPServers)) + } +} + +func TestResolvedWorkerRuntimeWithConfigUsesStoredAgentNameForResumeMCPMaterialization(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[agent]] +name = "ant" +dir = "myrig" +provider = "stub" +session = "acp" +work_dir = ".gc/worktrees/{{.Rig}}/ants/{{.AgentBase}}" +min_active_sessions = 0 +max_active_sessions = 4 + +[providers.stub] +command = "/bin/echo" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + writeCatalogFile(t, cityDir, "mcp/identity.template.toml", ` +name = "identity" +command = "/bin/mcp" +args = ["{{.AgentName}}", "{{.WorkDir}}", "{{.TemplateName}}"] +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + workDir := filepath.Join(cityDir, ".gc", "worktrees", "myrig", "ants", "ant") + resolved, err := resolvedWorkerRuntimeWithConfig(cityDir, cfg, session.Info{ + Template: "myrig/ant", + Alias: "ant", + AgentName: "myrig/ant-adhoc-123", + Transport: "acp", + WorkDir: workDir, + }, "") + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfig: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfig() = nil") + } + if len(resolved.Hints.MCPServers) != 1 { + t.Fatalf("Hints.MCPServers len = %d, want 1", len(resolved.Hints.MCPServers)) + } + if got, want := resolved.Hints.MCPServers[0].Args[0], "myrig/ant-adhoc-123"; got != want { + t.Fatalf("Args[0] = %q, want %q", got, want) + } + if got, want := resolved.Hints.MCPServers[0].Args[1], workDir; got != want { + t.Fatalf("Args[1] = %q, want %q", got, want) + } + if got, want := resolved.Hints.MCPServers[0].Args[2], "myrig/ant"; got != want { + t.Fatalf("Args[2] = %q, want %q", got, want) + } +} + +func TestResolvedWorkerRuntimeWithConfigFallsBackToStoredMCPServersWhenCatalogBreaks(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[agent]] +name = "ant" +dir = "myrig" +provider = "stub" +session = "acp" +work_dir = ".gc/worktrees/{{.Rig}}/ants/{{.AgentBase}}" +min_active_sessions = 0 +max_active_sessions = 4 + +[providers.stub] +command = "/bin/echo" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + writeCatalogFile(t, cityDir, "mcp/identity.template.toml", ` +name = "identity" +command = [broken +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + workDir := filepath.Join(cityDir, ".gc", "worktrees", "myrig", "ants", "ant") + metadata, err := session.WithStoredMCPMetadata(nil, "myrig/ant-adhoc-123", []runtime.MCPServerConfig{{ + Name: "identity", + Transport: runtime.MCPTransportStdio, + Command: "/bin/mcp", + Args: []string{"myrig/ant-adhoc-123", workDir, "myrig/ant"}, + }}) + if err != nil { + t.Fatalf("WithStoredMCPMetadata: %v", err) + } + + resolved, err := resolvedWorkerRuntimeWithConfigAndMetadata(cityDir, cfg, session.Info{ + Template: "myrig/ant", + Alias: "ant", + AgentName: "myrig/ant-adhoc-123", + Transport: "acp", + WorkDir: workDir, + }, "", metadata) + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfigAndMetadata: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfigAndMetadata() = nil") + } + if len(resolved.Hints.MCPServers) != 1 { + t.Fatalf("Hints.MCPServers len = %d, want 1", len(resolved.Hints.MCPServers)) + } + if got, want := resolved.Hints.MCPServers[0].Args[0], "myrig/ant-adhoc-123"; got != want { + t.Fatalf("Args[0] = %q, want %q", got, want) + } + if got, want := resolved.Hints.MCPServers[0].Args[1], workDir; got != want { + t.Fatalf("Args[1] = %q, want %q", got, want) + } + if got, want := resolved.Hints.MCPServers[0].Args[2], "myrig/ant"; got != want { + t.Fatalf("Args[2] = %q, want %q", got, want) + } +} + +func TestResolvedWorkerRuntimeWithConfigFallsBackToRuntimeMCPServersSnapshotWhenCatalogBreaks(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[agent]] +name = "ant" +dir = "myrig" +provider = "stub" +session = "acp" +work_dir = ".gc/worktrees/{{.Rig}}/ants/{{.AgentBase}}" +min_active_sessions = 0 +max_active_sessions = 4 + +[providers.stub] +command = "/bin/echo" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + writeCatalogFile(t, cityDir, "mcp/identity.template.toml", ` +name = "identity" +command = [broken +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + workDir := filepath.Join(cityDir, ".gc", "worktrees", "myrig", "ants", "ant") + servers := []runtime.MCPServerConfig{{ + Name: "identity", + Transport: runtime.MCPTransportHTTP, + Command: "/bin/mcp", + Args: []string{"--api-key", "super-secret"}, + Env: map[string]string{ + "API_TOKEN": "super-secret", + }, + URL: "https://user:pass@example.invalid/mcp?token=abc123", + Headers: map[string]string{ + "Authorization": "Bearer secret", + }, + }} + metadata, err := session.WithStoredMCPMetadata(nil, "myrig/ant-adhoc-123", servers) + if err != nil { + t.Fatalf("WithStoredMCPMetadata: %v", err) + } + if err := session.PersistRuntimeMCPServersSnapshot(cityDir, "sess-1", servers); err != nil { + t.Fatalf("PersistRuntimeMCPServersSnapshot: %v", err) + } + + resolved, err := resolvedWorkerRuntimeWithConfigAndMetadata(cityDir, cfg, session.Info{ + ID: "sess-1", + Template: "myrig/ant", + Alias: "ant", + AgentName: "myrig/ant-adhoc-123", + Transport: "acp", + WorkDir: workDir, + }, "", metadata) + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfigAndMetadata: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfigAndMetadata() = nil") + } + if len(resolved.Hints.MCPServers) != 1 { + t.Fatalf("Hints.MCPServers len = %d, want 1", len(resolved.Hints.MCPServers)) + } + if got, want := resolved.Hints.MCPServers[0].Args[1], "super-secret"; got != want { + t.Fatalf("Args[1] = %q, want %q", got, want) + } + if got, want := resolved.Hints.MCPServers[0].Env["API_TOKEN"], "super-secret"; got != want { + t.Fatalf("Env[API_TOKEN] = %q, want %q", got, want) + } + if got, want := resolved.Hints.MCPServers[0].Headers["Authorization"], "Bearer secret"; got != want { + t.Fatalf("Headers[Authorization] = %q, want %q", got, want) + } +} + +func TestResolvedWorkerRuntimeWithConfigFallsBackToSanitizedStoredMCPServersWhenRuntimeSnapshotMissing(t *testing.T) { + cityDir := t.TempDir() + writePhase0InterfaceCity(t, cityDir, `[workspace] +name = "test-city" + +[beads] +provider = "file" + +[[agent]] +name = "ant" +dir = "myrig" +provider = "stub" +session = "acp" +work_dir = ".gc/worktrees/{{.Rig}}/ants/{{.AgentBase}}" +min_active_sessions = 0 +max_active_sessions = 4 + +[providers.stub] +command = "/bin/echo" +supports_acp = true +acp_command = "/bin/echo" +acp_args = ["acp"] +`) + writeCatalogFile(t, cityDir, "mcp/identity.template.toml", ` +name = "identity" +command = [broken +`) + + cfg, err := loadCityConfig(cityDir) + if err != nil { + t.Fatalf("loadCityConfig: %v", err) + } + + workDir := filepath.Join(cityDir, ".gc", "worktrees", "myrig", "ants", "ant") + metadata, err := session.WithStoredMCPMetadata(nil, "myrig/ant-adhoc-123", []runtime.MCPServerConfig{{ + Name: "identity", + Transport: runtime.MCPTransportHTTP, + Command: "/bin/mcp", + Args: []string{"--serve", "--api-key", "super-secret"}, + Env: map[string]string{ + "API_TOKEN": "super-secret", + }, + URL: "https://user:pass@example.invalid/mcp?token=abc123", + Headers: map[string]string{ + "Authorization": "Bearer secret", + }, + }}) + if err != nil { + t.Fatalf("WithStoredMCPMetadata: %v", err) + } + + resolved, err := resolvedWorkerRuntimeWithConfigAndMetadata(cityDir, cfg, session.Info{ + ID: "sess-1", + Template: "myrig/ant", + Alias: "ant", + AgentName: "myrig/ant-adhoc-123", + Transport: "acp", + WorkDir: workDir, + }, "", metadata) + if err != nil { + t.Fatalf("resolvedWorkerRuntimeWithConfigAndMetadata: %v", err) + } + if resolved == nil { + t.Fatal("resolvedWorkerRuntimeWithConfigAndMetadata() = nil") + } + if len(resolved.Hints.MCPServers) != 1 { + t.Fatalf("Hints.MCPServers len = %d, want 1", len(resolved.Hints.MCPServers)) + } + if got, want := resolved.Hints.MCPServers[0].Args, []string{"--serve"}; len(got) != len(want) || got[0] != want[0] { + t.Fatalf("Args = %#v, want %#v", got, want) + } + if len(resolved.Hints.MCPServers[0].Env) != 0 { + t.Fatalf("Env = %#v, want empty", resolved.Hints.MCPServers[0].Env) + } + if len(resolved.Hints.MCPServers[0].Headers) != 0 { + t.Fatalf("Headers = %#v, want empty", resolved.Hints.MCPServers[0].Headers) + } + if got, want := resolved.Hints.MCPServers[0].URL, "https://example.invalid/mcp"; got != want { + t.Fatalf("URL = %q, want %q", got, want) + } +} + func TestWorkerSessionRuntimeResolverWithConfigFallsBackToProviderNameWhenResolvedCommandMissing(t *testing.T) { cfg := &config.City{ Workspace: config.Workspace{Name: "test-city"}, @@ -517,7 +1540,7 @@ func TestWorkerSessionRuntimeResolverWithConfigFallsBackToProviderNameWhenResolv t.Fatal("workerSessionRuntimeResolverWithConfig() = nil") } - runtimeCfg, err := resolver(session.Info{Template: "worker"}, "") + runtimeCfg, err := resolver(session.Info{Template: "worker"}, "", nil) if err != nil { t.Fatalf("resolver: %v", err) } @@ -562,7 +1585,7 @@ func TestWorkerSessionRuntimeResolverWithConfigFallsBackToPersistedRuntimeOnInco ResumeCommand: "persisted resume {{.SessionKey}}", } - runtimeCfg, err := resolver(info, "") + runtimeCfg, err := resolver(info, "", nil) if err != nil { t.Fatalf("resolver: %v", err) } @@ -622,7 +1645,7 @@ func TestWorkerSessionRuntimeResolverWithConfigFallsBackToPersistedProviderWhenC Provider: "persisted-provider", } - runtimeCfg, err := resolver(info, "") + runtimeCfg, err := resolver(info, "", nil) if err != nil { t.Fatalf("resolver: %v", err) } diff --git a/cmd/gen-client/main.go b/cmd/gen-client/main.go index 12dc63c52a..98c85f1596 100644 --- a/cmd/gen-client/main.go +++ b/cmd/gen-client/main.go @@ -11,6 +11,8 @@ // The routes we register ARE the routes we expose. Every schema and // path in the generated client matches what the server publishes to // external consumers — no hidden rename, no hidden path rewrite. +// skip-prune keeps documentation-only compatibility components +// available to in-tree callers that still deserialize those shapes. // 3. Write the generated client to internal/api/genclient/client_gen.go. // // Usage: @@ -42,7 +44,7 @@ func main() { func run() error { // Step 1: fetch the 3.0-downgraded spec from the supervisor. - sm := api.NewSupervisorMux(emptyResolver{}, false, "", time.Time{}) + sm := api.NewSupervisorMux(emptyResolver{}, nil, false, "", time.Time{}) req := httptest.NewRequest(http.MethodGet, "/openapi-3.0.json", nil) rec := httptest.NewRecorder() sm.ServeHTTP(rec, req) @@ -66,7 +68,7 @@ func run() error { // Step 3: invoke oapi-codegen. Output goes to stdout — the caller // redirects it to internal/api/genclient/client_gen.go. - cmd := exec.Command("oapi-codegen", "-generate", "types,client", "-package", "genclient", tmp.Name()) + cmd := exec.Command("oapi-codegen", "-generate", "types,client,skip-prune", "-package", "genclient", tmp.Name()) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { diff --git a/cmd/genspec/main.go b/cmd/genspec/main.go index 717d023610..44a02782e7 100644 --- a/cmd/genspec/main.go +++ b/cmd/genspec/main.go @@ -43,7 +43,10 @@ func main() { flag.BoolVar(&stdoutFlag, "stdout", false, "Write the spec to stdout instead of disk.") flag.Parse() - sm := api.NewSupervisorMux(emptyResolver{}, false, "", time.Time{}) + // Spec generation does not exercise city creation; nil Initializer + // leaves POST /v0/city returning 501 in the live spec, which is + // not observable at spec generation time. + sm := api.NewSupervisorMux(emptyResolver{}, nil, false, "", time.Time{}) req := httptest.NewRequest(http.MethodGet, "/openapi.json", nil) rec := httptest.NewRecorder() sm.ServeHTTP(rec, req) diff --git a/contrib/session-scripts/gc-session-k8s b/contrib/session-scripts/gc-session-k8s index db8f0acf08..01afb52a1a 100755 --- a/contrib/session-scripts/gc-session-k8s +++ b/contrib/session-scripts/gc-session-k8s @@ -304,10 +304,8 @@ case "$op" in # differs from the host work_dir, keep a compatibility mount at the original # city path for any remaining absolute-path references in staged files. main_vol_mounts='[{name: "ws", mountPath: "/workspace"}, {name: "claude-config", mountPath: "/tmp/claude-secret", readOnly: true}]' - extra_jq_args=() if [ -n "$gc_city" ] && [ "$gc_city" != "$work_dir" ]; then main_vol_mounts='[{name: "ws", mountPath: "/workspace"}, {name: "city", mountPath: $city}, {name: "claude-config", mountPath: "/tmp/claude-secret", readOnly: true}]' - extra_jq_args+=(--arg city "$gc_city") fi if [ "$needs_staging" = "true" ]; then @@ -334,7 +332,7 @@ case "$op" in --arg work_dir "$pod_work_dir" \ --arg sa "$SERVICE_ACCOUNT" \ --argjson env "$env_array" \ - ${extra_jq_args[@]+"${extra_jq_args[@]}"} \ + --arg city "$gc_city" \ '{ apiVersion: "v1", kind: "Pod", @@ -402,7 +400,7 @@ case "$op" in --arg work_dir "$pod_work_dir" \ --arg sa "$SERVICE_ACCOUNT" \ --argjson env "$env_array" \ - ${extra_jq_args[@]+"${extra_jq_args[@]}"} \ + --arg city "$gc_city" \ '{ apiVersion: "v1", kind: "Pod", diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 9736df2abe..044c2413e6 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -40,8 +40,13 @@ The exact versions CI pins are in [`deps.env`](https://github.com/gastownhall/ga brew install gastownhall/gascity/gascity ``` -This taps the `gastownhall/gascity` formula, builds or fetches the `gc` binary, -and installs all six runtime dependencies (tmux, jq, git, dolt, flock, beads). +This taps the `gastownhall/gascity` formula, downloads the matching `gc` +release asset, and installs all six runtime dependencies (tmux, jq, git, dolt, +flock, beads). + +Once Gas City is accepted into homebrew-core, the normal install path will be +`brew install gascity`; the `gastownhall/gascity` tap remains available for +emergency updates. Verify the installation: diff --git a/docs/reference/api.md b/docs/reference/api.md index 7f43ea222a..14fa31b8f2 100644 --- a/docs/reference/api.md +++ b/docs/reference/api.md @@ -42,6 +42,43 @@ The spec is the full reference. A brief summary of the surfaces: - **Config & packs.** Per-city config and pack metadata under `/v0/city/{cityName}/config` and `/v0/city/{cityName}/packs`. +## Request and response headers + +Every operation's header contract appears in the OpenAPI spec — if a +request header is required or a response header is promised, the +spec describes it. The two cross-cutting headers every API client +should know about: + +- **`X-GC-Request`** (request header, required on all mutations). + Anti-CSRF token required on every POST, PUT, PATCH, and DELETE. + Any non-empty value is accepted; the header's presence is what + the server checks. Requests without it are rejected with + `403 csrf: X-GC-Request header required on mutation endpoints`. + Leveraging the same-origin policy, a cross-origin attacker + cannot set this header on a forged request. The generated Go + and TypeScript clients set this header automatically; only raw + HTTP clients need to remember it. +- **`X-GC-Request-Id`** (response header, every response). + Opaque per-response identifier the server assigns for log + correlation. Every response — success or error — carries this + header; the spec declares it via a `$ref` to + `components.headers.X-GC-Request-Id`. Include its value in bug + reports so the server's logs can be traced. + +SSE stream operations emit additional runtime-status headers before +the first event frame: + +- **`stream-agent-output` / `stream-agent-output-qualified`**: + `GC-Agent-Status` — set to `stopped` when the agent is not + running and the stream is replaying transcript from the session + log instead of live output. +- **`stream-session`**: `GC-Session-State` (e.g. `active`, + `closed`) and `GC-Session-Status` (`stopped` when the session's + underlying process is not running). + +Each header's schema is documented in the operation's +`responses.200.headers` in the spec. + ## Errors Every error response is an RFC 9457 Problem Details body @@ -70,6 +107,128 @@ closes immediately. For example, `GET /v0/events/stream` returns `503 application/problem+json` with `detail: "no_providers: ..."` when no running city has an event provider registered. +## Creating a city (asynchronous) + +`POST /v0/city` is an **asynchronous** operation. The response is +`202 Accepted` returned as soon as the city has been scaffolded on +disk and registered with the supervisor. The slow finalize work +(pack materialization, bead store startup, formula resolution, +agent validation) runs on the supervisor reconciler's next tick. +Clients observe completion via the supervisor event stream — there +is nothing to poll. + +### Response + +```json +{ + "ok": true, + "name": "my-city", + "path": "/abs/path/to/my-city" +} +``` + +The `name` field is the city's resolved runtime identity +(`workspace.name` from `city.toml`, or the directory basename). +Use it to filter the event stream for completion. + +### Completion events + +On the same `/v0/events/stream` the client will see (in order): + +- `city.created` (`CityCreatedPayload`) — emitted by the scaffold + step before `POST` returns. `subject` and payload `name` equal + the response's `name`. +- `city.ready` (`CityReadyPayload`) — the reconciler finished + `prepareCityForSupervisor` successfully. Matching event: + `subject == name` and `type == "city.ready"`. +- `city.init_failed` (`CityInitFailedPayload`) — the reconciler + gave up. The payload's `error` field describes why, including + deferred dependency or provider-readiness blockers that the async + API does not fail synchronously. + +Exactly one of `city.ready` or `city.init_failed` lands per +successful `POST`. Clients wait for either; no polling of +`GET /v0/cities` or `GET /v0/city/{cityName}/readiness` is +required. + +### Subscribe before or after POST + +Either order works. The recommended flow is: + +1. `POST /v0/city` and wait for `202`. +2. `GET /v0/events/stream?after_cursor=0` — request replay from + the start so `city.created` (and possibly `city.ready`) are + delivered even if they fired before subscribe. +3. Read frames until `subject == response.name` and + `type ∈ {"city.ready", "city.init_failed"}`. + +**Empty supervisor is fine.** The event stream works even when +no cities existed before the `POST`. `POST` writes the city to +the supervisor registry (`cities.toml`) and creates +`.gc/events.jsonl` synchronously before returning 202, so the +event multiplexer finds the new city on the very next +`buildMultiplexer` call. Subscribers do **not** need to retry on +`503 no_providers`; if that error surfaces after a successful +202, it's a bug. + +### Errors + +- `409 conflict: city already initialized at ` — the target + directory already has a scaffolded city. +- `422` — invalid provider, invalid bootstrap profile, or other + body-validation failure. +- `503` — a hard dependency is missing on the host, or a provider + the city needs is not ready. +- `500` — unexpected scaffold failure; consult the server logs + via the `X-GC-Request-Id` correlation header. + +## Unregistering a city (asynchronous) + +`POST /v0/city/{cityName}/unregister` removes a city from the +supervisor's registry and signals the supervisor to stop the city's +controller. Like `POST /v0/city`, it is asynchronous: the response +is `202 Accepted` returned as soon as the registry entry is gone +and the supervisor is notified. The supervisor reconciler stops the +controller on its next tick and emits the completion event. + +The city directory on disk is **not** touched. This operation only +detaches the city from the supervisor; reattaching it later is a +simple `gc register`. + +### Response + +```json +{ + "ok": true, + "name": "my-city", + "path": "/abs/path/to/my-city" +} +``` + +### Completion events + +On `/v0/events/stream` the client will see (in order): + +- `city.unregister_requested` + (`CityUnregisterRequestedPayload`) — emitted by the handler + before the registry write so subscribers see the teardown start. +- `city.unregistered` (`CityUnregisteredPayload`) — emitted by the + reconciler once the city's controller has stopped. Matching + event: `subject == name` and `type == "city.unregistered"`. +- `city.unregister_failed` (`CityUnregisterFailedPayload`) — emitted + by the reconciler if the controller did not stop cleanly. The + payload's `error` field describes the failure. + +Exactly one of `city.unregistered` or `city.unregister_failed` +lands per successful unregister. Clients wait for either. + +### Errors + +- `404 not_found: city not registered with supervisor: ` — no + entry in the registry for that name. +- `501` — supervisor has no Initializer wired (test-only configs). +- `500` — unexpected registry write failure. + ## Event Contract The event APIs, the SSE streams, and `gc events` are the same contract diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 1f49fec05a..32f37161f2 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -24,6 +24,7 @@ gc [flags] | [gc beads](#gc-beads) | Manage the beads provider | | [gc build-image](#gc-build-image) | Build a prebaked agent container image | | [gc cities](#gc-cities) | List registered cities | +| [gc completion](#gc-completion) | Generate the autocompletion script for the specified shell | | [gc config](#gc-config) | Inspect and validate city configuration | | [gc converge](#gc-converge) | Manage convergence loops (bounded iterative refinement) | | [gc convoy](#gc-convoy) | Manage convoys — graphs of related work | @@ -52,6 +53,7 @@ gc [flags] | [gc runtime](#gc-runtime) | Process-intrinsic runtime operations | | [gc service](#gc-service) | Inspect workspace services | | [gc session](#gc-session) | Manage interactive chat sessions | +| [gc shell](#gc-shell) | Manage the Gas City shell integration hook | | [gc skill](#gc-skill) | List visible skills | | [gc sling](#gc-sling) | Route work to a session config or agent | | [gc start](#gc-start) | Start the city under the machine-wide supervisor | @@ -304,6 +306,127 @@ List registered cities gc cities list ``` +## gc completion + +Generate the autocompletion script for gc for the specified shell. +See each sub-command's help for details on how to use the generated script. + +``` +gc completion +``` + +| Subcommand | Description | +|------------|-------------| +| [gc completion bash](#gc-completion-bash) | Generate the autocompletion script for bash | +| [gc completion fish](#gc-completion-fish) | Generate the autocompletion script for fish | +| [gc completion powershell](#gc-completion-powershell) | Generate the autocompletion script for powershell | +| [gc completion zsh](#gc-completion-zsh) | Generate the autocompletion script for zsh | + +## gc completion bash + +Generate the autocompletion script for the bash shell. + +This script depends on the 'bash-completion' package. +If it is not installed already, you can install it via your OS's package manager. + +To load completions in your current shell session: + + source <(gc completion bash) + +To load completions for every new session, execute once: + +#### Linux: + + gc completion bash > /etc/bash_completion.d/gc + +#### macOS: + + gc completion bash > $(brew --prefix)/etc/bash_completion.d/gc + +You will need to start a new shell for this setup to take effect. + +``` +gc completion bash +``` + +| Flag | Type | Default | Description | +|------|------|---------|-------------| +| `--no-descriptions` | bool | | disable completion descriptions | + +## gc completion fish + +Generate the autocompletion script for the fish shell. + +To load completions in your current shell session: + + gc completion fish | source + +To load completions for every new session, execute once: + + gc completion fish > ~/.config/fish/completions/gc.fish + +You will need to start a new shell for this setup to take effect. + +``` +gc completion fish [flags] +``` + +| Flag | Type | Default | Description | +|------|------|---------|-------------| +| `--no-descriptions` | bool | | disable completion descriptions | + +## gc completion powershell + +Generate the autocompletion script for powershell. + +To load completions in your current shell session: + + gc completion powershell | Out-String | Invoke-Expression + +To load completions for every new session, add the output of the above command +to your powershell profile. + +``` +gc completion powershell [flags] +``` + +| Flag | Type | Default | Description | +|------|------|---------|-------------| +| `--no-descriptions` | bool | | disable completion descriptions | + +## gc completion zsh + +Generate the autocompletion script for the zsh shell. + +If shell completion is not already enabled in your environment you will need +to enable it. You can execute the following once: + + echo "autoload -U compinit; compinit" >> ~/.zshrc + +To load completions in your current shell session: + + source <(gc completion zsh) + +To load completions for every new session, execute once: + +#### Linux: + + gc completion zsh > "${fpath[1]}/_gc" + +#### macOS: + + gc completion zsh > $(brew --prefix)/share/zsh/site-functions/_gc + +You will need to start a new shell for this setup to take effect. + +``` +gc completion zsh [flags] +``` + +| Flag | Type | Default | Description | +|------|------|---------|-------------| +| `--no-descriptions` | bool | | disable completion descriptions | + ## gc config Inspect, validate, and debug the resolved city configuration. @@ -2249,6 +2372,51 @@ gc session wake gc-42 gc session wake mayor ``` +## gc shell + +The shell integration adds a completion hook to your shell RC file that +provides tab-completion for gc commands and flags. + +Subcommands: install, remove, status. + +``` +gc shell +``` + +| Subcommand | Description | +|------------|-------------| +| [gc shell install](#gc-shell-install) | Install or update shell integration | +| [gc shell remove](#gc-shell-remove) | Remove shell integration | +| [gc shell status](#gc-shell-status) | Show shell integration status | + +## gc shell install + +Install or update the gc shell completion hook. + +If no shell is specified, the shell is detected from $SHELL. +The completion script is written to ~/.gc/completions/ and a source line +is added to your shell RC file. + +``` +gc shell install [bash|zsh|fish] +``` + +## gc shell remove + +Remove the gc shell completion hook from your shell RC file and delete the completion script. + +``` +gc shell remove +``` + +## gc shell status + +Show shell integration status + +``` +gc shell status +``` + ## gc skill List skills visible to the current city. @@ -2315,7 +2483,7 @@ gc sling [target] [flags] | Flag | Type | Default | Description | |------|------|---------|-------------| | `-n`, `--dry-run` | bool | | show what would be done without executing | -| `--force` | bool | | suppress warnings and allow cross-rig routing | +| `--force` | bool | | suppress warnings, allow cross-rig routing, allow graph workflow replacement, and for direct bead routes dispatch even if the bead does not resolve in the local store | | `-f`, `--formula` | bool | | treat argument as formula name | | `--merge` | string | | merge strategy: direct, mr, or local | | `--no-convoy` | bool | | skip auto-convoy creation | diff --git a/docs/reference/config.md b/docs/reference/config.md index 4181018c32..f4b92800a6 100644 --- a/docs/reference/config.md +++ b/docs/reference/config.md @@ -434,7 +434,9 @@ ProviderPatch modifies an existing provider identified by Name. | `name` | string | **yes** | | Name is the targeting key (required). Must match an existing provider's name. | | `base` | string | | | Base overrides the provider's inheritance parent (presence-aware). Pointer to a pointer so the patch can distinguish "no change" (double-nil) from "clear to inherit default" (single-nil value in outer pointer) from "set to explicit empty opt-out" (value "" in inner pointer) from "set to <name>". Callers use: nil = patch does not touch Base &(*string)(nil) = patch clears Base to absent &(&"") = patch sets Base = "" (explicit opt-out) &(&"builtin:codex") = patch sets Base to that value | | `command` | string | | | Command overrides the provider command. | +| `acp_command` | string | | | ACPCommand overrides the provider command for ACP transport sessions. | | `args` | []string | | | Args overrides the provider args. | +| `acp_args` | []string | | | ACPArgs overrides the provider args for ACP transport sessions. | | `args_append` | []string | | | ArgsAppend overrides the provider args_append list. | | `options_schema_merge` | string | | | OptionsSchemaMerge overrides the options_schema merge mode. | | `prompt_mode` | string | | | PromptMode overrides prompt delivery mode. Enum: `arg`, `flag`, `none` | @@ -476,6 +478,8 @@ ProviderSpec defines a named provider's startup parameters. | `options_schema` | []ProviderOption | | | OptionsSchema declares the configurable options this provider supports. Each option maps to CLI args via its Choices[].FlagArgs field. Serialized via a dedicated DTO (not directly to JSON) so FlagArgs stays server-side. | | `print_args` | []string | | | PrintArgs are CLI arguments that enable one-shot non-interactive mode. The provider prints its response to stdout and exits. When empty, the provider does not support one-shot invocation. Examples: ["-p"] (claude, gemini), ["exec"] (codex) | | `title_model` | string | | | TitleModel is the OptionsSchema model key used for title generation. Resolved via the "model" option in OptionsSchema to get FlagArgs. Defaults to the cheapest/fastest model for each provider. Examples: "haiku" (claude), "o4-mini" (codex), "gemini-2.5-flash" (gemini) | +| `acp_command` | string | | | ACPCommand overrides Command when the session transport is ACP. When empty, Command is used for both tmux and ACP transports. | +| `acp_args` | []string | | | ACPArgs overrides Args when the session transport is ACP. When nil, Args is used for both tmux and ACP transports. | ## Rig diff --git a/docs/reference/exec-beads-provider.md b/docs/reference/exec-beads-provider.md index ef1afd6348..138e58f372 100644 --- a/docs/reference/exec-beads-provider.md +++ b/docs/reference/exec-beads-provider.md @@ -296,9 +296,11 @@ Stdout: the root bead ID as plain text (e.g., `WP-42\n`). ### Status Mapping -Gas City uses 3 statuses: `open`, `in_progress`, `closed`. The exec -script must normalize its backend's statuses to these three. For example, -`bd` maps `blocked`, `review`, and `testing` to `open`. +Gas City's `beads.Store` surface uses the SDK's three-state vocabulary: +`open`, `in_progress`, and `closed`. Backends that expose a richer status +set must map it onto those three values. The built-in `BdStore`, for +example, maps bd's `blocked`, `review`, and `testing` states to `open`. +An empty status is also treated as `open`. ## Implementation Plan diff --git a/docs/schema/city-schema.json b/docs/schema/city-schema.json index e6aaa40de6..cdb08ca4a9 100644 --- a/docs/schema/city-schema.json +++ b/docs/schema/city-schema.json @@ -1491,6 +1491,10 @@ "type": "string", "description": "Command overrides the provider command." }, + "acp_command": { + "type": "string", + "description": "ACPCommand overrides the provider command for ACP transport sessions." + }, "args": { "items": { "type": "string" @@ -1498,6 +1502,13 @@ "type": "array", "description": "Args overrides the provider args." }, + "acp_args": { + "items": { + "type": "string" + }, + "type": "array", + "description": "ACPArgs overrides the provider args for ACP transport sessions." + }, "args_append": { "items": { "type": "string" @@ -1693,6 +1704,17 @@ "title_model": { "type": "string", "description": "TitleModel is the OptionsSchema model key used for title generation.\nResolved via the \"model\" option in OptionsSchema to get FlagArgs.\nDefaults to the cheapest/fastest model for each provider.\nExamples: \"haiku\" (claude), \"o4-mini\" (codex), \"gemini-2.5-flash\" (gemini)" + }, + "acp_command": { + "type": "string", + "description": "ACPCommand overrides Command when the session transport is ACP.\nWhen empty, Command is used for both tmux and ACP transports." + }, + "acp_args": { + "items": { + "type": "string" + }, + "type": "array", + "description": "ACPArgs overrides Args when the session transport is ACP.\nWhen nil, Args is used for both tmux and ACP transports." } }, "additionalProperties": false, diff --git a/docs/schema/city-schema.txt b/docs/schema/city-schema.txt index e6aaa40de6..cdb08ca4a9 100644 --- a/docs/schema/city-schema.txt +++ b/docs/schema/city-schema.txt @@ -1491,6 +1491,10 @@ "type": "string", "description": "Command overrides the provider command." }, + "acp_command": { + "type": "string", + "description": "ACPCommand overrides the provider command for ACP transport sessions." + }, "args": { "items": { "type": "string" @@ -1498,6 +1502,13 @@ "type": "array", "description": "Args overrides the provider args." }, + "acp_args": { + "items": { + "type": "string" + }, + "type": "array", + "description": "ACPArgs overrides the provider args for ACP transport sessions." + }, "args_append": { "items": { "type": "string" @@ -1693,6 +1704,17 @@ "title_model": { "type": "string", "description": "TitleModel is the OptionsSchema model key used for title generation.\nResolved via the \"model\" option in OptionsSchema to get FlagArgs.\nDefaults to the cheapest/fastest model for each provider.\nExamples: \"haiku\" (claude), \"o4-mini\" (codex), \"gemini-2.5-flash\" (gemini)" + }, + "acp_command": { + "type": "string", + "description": "ACPCommand overrides Command when the session transport is ACP.\nWhen empty, Command is used for both tmux and ACP transports." + }, + "acp_args": { + "items": { + "type": "string" + }, + "type": "array", + "description": "ACPArgs overrides Args when the session transport is ACP.\nWhen nil, Args is used for both tmux and ACP transports." } }, "additionalProperties": false, diff --git a/docs/schema/openapi.json b/docs/schema/openapi.json index ee1f1e44e7..dcac755149 100644 --- a/docs/schema/openapi.json +++ b/docs/schema/openapi.json @@ -1,5 +1,14 @@ { "components": { + "headers": { + "X-GC-Request-Id": { + "description": "Opaque per-response identifier assigned by the server for log correlation. Every response carries this header.", + "schema": { + "description": "Opaque per-response identifier assigned by the server for log correlation. Every response carries this header.", + "type": "string" + } + } + }, "schemas": { "AdapterCapabilities": { "additionalProperties": false, @@ -665,6 +674,15 @@ "AnnotatedProviderResponse": { "additionalProperties": false, "properties": { + "acp_args": { + "items": { + "type": "string" + }, + "type": "array" + }, + "acp_command": { + "type": "string" + }, "args": { "items": { "type": "string" @@ -818,6 +836,17 @@ "null" ] }, + "metadata": { + "additionalProperties": { + "type": "string" + }, + "description": "Metadata key-value pairs to set at create time.", + "type": "object" + }, + "parent": { + "description": "Parent bead ID.", + "type": "string" + }, "priority": { "description": "Bead priority.", "format": "int64", @@ -932,6 +961,13 @@ "description": "Metadata key-value pairs to set.", "type": "object" }, + "parent": { + "description": "Parent bead ID. Use null or an empty string to clear.", + "type": [ + "string", + "null" + ] + }, "priority": { "description": "Bead priority.", "format": "int64", @@ -1023,17 +1059,22 @@ "CityCreateResponse": { "additionalProperties": false, "properties": { + "name": { + "description": "Resolved city name as persisted in city.toml. Use this to filter the event stream for completion.", + "type": "string" + }, "ok": { - "description": "True on success.", + "description": "True when scaffolding + registration succeeded. Does not imply the city is ready yet; watch /v0/events/stream for city.ready.", "type": "boolean" }, "path": { - "description": "Resolved absolute path of the created city.", + "description": "Resolved absolute path of the created city directory.", "type": "string" } }, "required": [ "ok", + "name", "path" ], "type": "object" @@ -1117,6 +1158,34 @@ ], "type": "object" }, + "CityLifecyclePayload": { + "additionalProperties": false, + "properties": { + "error": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "phases_completed": { + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + "required": [ + "name", + "path" + ], + "type": "object" + }, "CityPatchInputBody": { "additionalProperties": false, "properties": { @@ -1127,6 +1196,29 @@ }, "type": "object" }, + "CityUnregisterResponse": { + "additionalProperties": false, + "properties": { + "name": { + "description": "Resolved registry name. Filter the event stream by this to observe completion.", + "type": "string" + }, + "ok": { + "description": "True when the registry entry was removed and the supervisor was signaled. Does not imply the city's controller has stopped yet; watch /v0/events/stream for city.unregistered.", + "type": "boolean" + }, + "path": { + "description": "Resolved absolute city directory. The directory itself is not modified; unregister only affects the supervisor's registry.", + "type": "string" + } + }, + "required": [ + "ok", + "name", + "path" + ], + "type": "object" + }, "ConfigAgentResponse": { "additionalProperties": false, "properties": { @@ -1796,10 +1888,16 @@ "default": "about:blank", "description": "A URI reference to human-readable documentation for the error.", "examples": [ - "https://example.com/errors/example" + "https://example.com/errors/example", + "urn:gascity:error:sling-missing-bead", + "urn:gascity:error:sling-cross-rig" ], "format": "uri", - "type": "string" + "type": "string", + "x-gascity-problem-types": [ + "urn:gascity:error:sling-missing-bead", + "urn:gascity:error:sling-cross-rig" + ] } }, "type": "object" @@ -1859,6 +1957,9 @@ { "$ref": "#/components/schemas/BoundEventPayload" }, + { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, { "$ref": "#/components/schemas/GroupCreatedEventPayload" }, @@ -4381,6 +4482,20 @@ "ProviderCreateInputBody": { "additionalProperties": false, "properties": { + "acp_args": { + "description": "ACP transport command arguments override.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "acp_command": { + "description": "ACP transport command binary override.", + "type": "string" + }, "args": { "description": "Command arguments.", "items": { @@ -4506,6 +4621,21 @@ "ProviderPatch": { "additionalProperties": false, "properties": { + "ACPArgs": { + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "ACPCommand": { + "type": [ + "string", + "null" + ] + }, "Args": { "items": { "type": "string" @@ -4587,7 +4717,9 @@ "Name", "Base", "Command", + "ACPCommand", "Args", + "ACPArgs", "ArgsAppend", "OptionsSchemaMerge", "PromptMode", @@ -4602,6 +4734,20 @@ "ProviderPatchSetInputBody": { "additionalProperties": false, "properties": { + "acp_args": { + "description": "Override ACP transport command arguments.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "acp_command": { + "description": "Override ACP transport command binary.", + "type": "string" + }, "args": { "description": "Override command arguments.", "items": { @@ -4747,6 +4893,15 @@ "ProviderResponse": { "additionalProperties": false, "properties": { + "acp_args": { + "items": { + "type": "string" + }, + "type": "array" + }, + "acp_command": { + "type": "string" + }, "args": { "items": { "type": "string" @@ -4798,6 +4953,15 @@ "ProviderSpecJSON": { "additionalProperties": false, "properties": { + "acp_args": { + "items": { + "type": "string" + }, + "type": "array" + }, + "acp_command": { + "type": "string" + }, "args": { "items": { "type": "string" @@ -4835,6 +4999,20 @@ "ProviderUpdateInputBody": { "additionalProperties": false, "properties": { + "acp_args": { + "description": "ACP transport command arguments override.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "acp_command": { + "description": "ACP transport command binary override.", + "type": "string" + }, "args": { "description": "Command arguments.", "items": { @@ -5825,6 +6003,10 @@ "description": "Bead ID to sling.", "type": "string" }, + "force": { + "description": "Bypass cross-rig guards; for direct bead routes, also bypass missing-bead validation. Formula-backed graph routes may replace existing live workflow roots but still require the source bead to exist.", + "type": "boolean" + }, "formula": { "description": "Formula name for workflow launch.", "type": "string" @@ -6335,24 +6517,190 @@ ], "type": "string" }, - "UnboundEventPayload": { - "additionalProperties": false, - "properties": { - "count": { - "format": "int64", - "type": "integer" + "TypedEventStreamEnvelope": { + "description": "Discriminated union of city event stream envelopes. Each variant constrains the envelope type and payload schema together.", + "discriminator": { + "mapping": { + "bead.closed": "#/components/schemas/TypedEventStreamEnvelopeBeadClosed", + "bead.created": "#/components/schemas/TypedEventStreamEnvelopeBeadCreated", + "bead.updated": "#/components/schemas/TypedEventStreamEnvelopeBeadUpdated", + "city.created": "#/components/schemas/TypedEventStreamEnvelopeCityCreated", + "city.init_failed": "#/components/schemas/TypedEventStreamEnvelopeCityInitFailed", + "city.ready": "#/components/schemas/TypedEventStreamEnvelopeCityReady", + "city.resumed": "#/components/schemas/TypedEventStreamEnvelopeCityResumed", + "city.suspended": "#/components/schemas/TypedEventStreamEnvelopeCitySuspended", + "city.unregister_failed": "#/components/schemas/TypedEventStreamEnvelopeCityUnregisterFailed", + "city.unregister_requested": "#/components/schemas/TypedEventStreamEnvelopeCityUnregisterRequested", + "city.unregistered": "#/components/schemas/TypedEventStreamEnvelopeCityUnregistered", + "controller.started": "#/components/schemas/TypedEventStreamEnvelopeControllerStarted", + "controller.stopped": "#/components/schemas/TypedEventStreamEnvelopeControllerStopped", + "convoy.closed": "#/components/schemas/TypedEventStreamEnvelopeConvoyClosed", + "convoy.created": "#/components/schemas/TypedEventStreamEnvelopeConvoyCreated", + "extmsg.adapter_added": "#/components/schemas/TypedEventStreamEnvelopeExtmsgAdapterAdded", + "extmsg.adapter_removed": "#/components/schemas/TypedEventStreamEnvelopeExtmsgAdapterRemoved", + "extmsg.bound": "#/components/schemas/TypedEventStreamEnvelopeExtmsgBound", + "extmsg.group_created": "#/components/schemas/TypedEventStreamEnvelopeExtmsgGroupCreated", + "extmsg.inbound": "#/components/schemas/TypedEventStreamEnvelopeExtmsgInbound", + "extmsg.outbound": "#/components/schemas/TypedEventStreamEnvelopeExtmsgOutbound", + "extmsg.unbound": "#/components/schemas/TypedEventStreamEnvelopeExtmsgUnbound", + "mail.archived": "#/components/schemas/TypedEventStreamEnvelopeMailArchived", + "mail.deleted": "#/components/schemas/TypedEventStreamEnvelopeMailDeleted", + "mail.marked_read": "#/components/schemas/TypedEventStreamEnvelopeMailMarkedRead", + "mail.marked_unread": "#/components/schemas/TypedEventStreamEnvelopeMailMarkedUnread", + "mail.read": "#/components/schemas/TypedEventStreamEnvelopeMailRead", + "mail.replied": "#/components/schemas/TypedEventStreamEnvelopeMailReplied", + "mail.sent": "#/components/schemas/TypedEventStreamEnvelopeMailSent", + "order.completed": "#/components/schemas/TypedEventStreamEnvelopeOrderCompleted", + "order.failed": "#/components/schemas/TypedEventStreamEnvelopeOrderFailed", + "order.fired": "#/components/schemas/TypedEventStreamEnvelopeOrderFired", + "provider.swapped": "#/components/schemas/TypedEventStreamEnvelopeProviderSwapped", + "session.crashed": "#/components/schemas/TypedEventStreamEnvelopeSessionCrashed", + "session.draining": "#/components/schemas/TypedEventStreamEnvelopeSessionDraining", + "session.idle_killed": "#/components/schemas/TypedEventStreamEnvelopeSessionIdleKilled", + "session.quarantined": "#/components/schemas/TypedEventStreamEnvelopeSessionQuarantined", + "session.stopped": "#/components/schemas/TypedEventStreamEnvelopeSessionStopped", + "session.suspended": "#/components/schemas/TypedEventStreamEnvelopeSessionSuspended", + "session.undrained": "#/components/schemas/TypedEventStreamEnvelopeSessionUndrained", + "session.updated": "#/components/schemas/TypedEventStreamEnvelopeSessionUpdated", + "session.woke": "#/components/schemas/TypedEventStreamEnvelopeSessionWoke", + "worker.operation": "#/components/schemas/TypedEventStreamEnvelopeWorkerOperation" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeBeadClosed" }, - "session_id": { - "type": "string" + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeBeadCreated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeBeadUpdated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityCreated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityInitFailed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityReady" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityResumed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCitySuspended" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityUnregisterFailed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityUnregisterRequested" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityUnregistered" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeControllerStarted" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeControllerStopped" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeConvoyClosed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeConvoyCreated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgAdapterAdded" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgAdapterRemoved" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgBound" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgGroupCreated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgInbound" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgOutbound" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgUnbound" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailArchived" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailDeleted" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailMarkedRead" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailMarkedUnread" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailRead" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailReplied" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailSent" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeOrderCompleted" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeOrderFailed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeOrderFired" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeProviderSwapped" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionCrashed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionDraining" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionIdleKilled" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionQuarantined" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionStopped" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionSuspended" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionUndrained" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionUpdated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionWoke" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeWorkerOperation" } - }, - "required": [ - "session_id", - "count" ], - "type": "object" + "title": "Typed city event stream envelope" }, - "WireEvent": { + "TypedEventStreamEnvelopeBeadClosed": { "additionalProperties": false, "properties": { "actor": { @@ -6362,7 +6710,7 @@ "type": "string" }, "payload": { - "$ref": "#/components/schemas/EventPayload" + "$ref": "#/components/schemas/BeadEventPayload" }, "seq": { "format": "int64", @@ -6377,31 +6725,34 @@ "type": "string" }, "type": { + "const": "bead.closed", "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" } }, "required": [ "seq", "type", "ts", - "actor" + "actor", + "payload" ], + "title": "TypedEventStreamEnvelope bead.closed", "type": "object" }, - "WireTaggedEvent": { + "TypedEventStreamEnvelopeBeadCreated": { "additionalProperties": false, "properties": { "actor": { "type": "string" }, - "city": { - "type": "string" - }, "message": { "type": "string" }, "payload": { - "$ref": "#/components/schemas/EventPayload" + "$ref": "#/components/schemas/BeadEventPayload" }, "seq": { "format": "int64", @@ -6416,23 +6767,4004 @@ "type": "string" }, "type": { + "const": "bead.created", "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" } }, "required": [ - "city", "seq", "type", "ts", - "actor" + "actor", + "payload" ], + "title": "TypedEventStreamEnvelope bead.created", "type": "object" }, - "WorkerOperationEventPayload": { + "TypedEventStreamEnvelopeBeadUpdated": { "additionalProperties": false, "properties": { - "delivered": { - "type": "boolean" + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BeadEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "bead.updated", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope bead.updated", + "type": "object" + }, + "TypedEventStreamEnvelopeCityCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.created", + "type": "object" + }, + "TypedEventStreamEnvelopeCityInitFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.init_failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.init_failed", + "type": "object" + }, + "TypedEventStreamEnvelopeCityReady": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.ready", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.ready", + "type": "object" + }, + "TypedEventStreamEnvelopeCityResumed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.resumed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.resumed", + "type": "object" + }, + "TypedEventStreamEnvelopeCitySuspended": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.suspended", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.suspended", + "type": "object" + }, + "TypedEventStreamEnvelopeCityUnregisterFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregister_failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.unregister_failed", + "type": "object" + }, + "TypedEventStreamEnvelopeCityUnregisterRequested": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregister_requested", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.unregister_requested", + "type": "object" + }, + "TypedEventStreamEnvelopeCityUnregistered": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregistered", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.unregistered", + "type": "object" + }, + "TypedEventStreamEnvelopeControllerStarted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "controller.started", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope controller.started", + "type": "object" + }, + "TypedEventStreamEnvelopeControllerStopped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "controller.stopped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope controller.stopped", + "type": "object" + }, + "TypedEventStreamEnvelopeConvoyClosed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "convoy.closed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope convoy.closed", + "type": "object" + }, + "TypedEventStreamEnvelopeConvoyCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "convoy.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope convoy.created", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgAdapterAdded": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/AdapterEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.adapter_added", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.adapter_added", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgAdapterRemoved": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/AdapterEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.adapter_removed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.adapter_removed", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgBound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BoundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.bound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.bound", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgGroupCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/GroupCreatedEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.group_created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.group_created", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgInbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/InboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.inbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.inbound", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgOutbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/OutboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.outbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.outbound", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgUnbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/UnboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.unbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.unbound", + "type": "object" + }, + "TypedEventStreamEnvelopeMailArchived": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.archived", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.archived", + "type": "object" + }, + "TypedEventStreamEnvelopeMailDeleted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.deleted", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.deleted", + "type": "object" + }, + "TypedEventStreamEnvelopeMailMarkedRead": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.marked_read", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.marked_read", + "type": "object" + }, + "TypedEventStreamEnvelopeMailMarkedUnread": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.marked_unread", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.marked_unread", + "type": "object" + }, + "TypedEventStreamEnvelopeMailRead": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.read", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.read", + "type": "object" + }, + "TypedEventStreamEnvelopeMailReplied": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.replied", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.replied", + "type": "object" + }, + "TypedEventStreamEnvelopeMailSent": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.sent", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.sent", + "type": "object" + }, + "TypedEventStreamEnvelopeOrderCompleted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.completed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope order.completed", + "type": "object" + }, + "TypedEventStreamEnvelopeOrderFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope order.failed", + "type": "object" + }, + "TypedEventStreamEnvelopeOrderFired": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.fired", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope order.fired", + "type": "object" + }, + "TypedEventStreamEnvelopeProviderSwapped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "provider.swapped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope provider.swapped", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionCrashed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.crashed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.crashed", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionDraining": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.draining", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.draining", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionIdleKilled": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.idle_killed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.idle_killed", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionQuarantined": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.quarantined", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.quarantined", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionStopped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.stopped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.stopped", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionSuspended": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.suspended", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.suspended", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionUndrained": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.undrained", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.undrained", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionUpdated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.updated", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.updated", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionWoke": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.woke", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.woke", + "type": "object" + }, + "TypedEventStreamEnvelopeWorkerOperation": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/WorkerOperationEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "worker.operation", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope worker.operation", + "type": "object" + }, + "TypedTaggedEventStreamEnvelope": { + "description": "Discriminated union of supervisor event stream envelopes. Each variant constrains the envelope type and payload schema together and includes the source city.", + "discriminator": { + "mapping": { + "bead.closed": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadClosed", + "bead.created": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadCreated", + "bead.updated": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadUpdated", + "city.created": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityCreated", + "city.init_failed": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityInitFailed", + "city.ready": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityReady", + "city.resumed": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityResumed", + "city.suspended": "#/components/schemas/TypedTaggedEventStreamEnvelopeCitySuspended", + "city.unregister_failed": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregisterFailed", + "city.unregister_requested": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregisterRequested", + "city.unregistered": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregistered", + "controller.started": "#/components/schemas/TypedTaggedEventStreamEnvelopeControllerStarted", + "controller.stopped": "#/components/schemas/TypedTaggedEventStreamEnvelopeControllerStopped", + "convoy.closed": "#/components/schemas/TypedTaggedEventStreamEnvelopeConvoyClosed", + "convoy.created": "#/components/schemas/TypedTaggedEventStreamEnvelopeConvoyCreated", + "extmsg.adapter_added": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded", + "extmsg.adapter_removed": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved", + "extmsg.bound": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgBound", + "extmsg.group_created": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgGroupCreated", + "extmsg.inbound": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgInbound", + "extmsg.outbound": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgOutbound", + "extmsg.unbound": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgUnbound", + "mail.archived": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailArchived", + "mail.deleted": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailDeleted", + "mail.marked_read": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailMarkedRead", + "mail.marked_unread": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailMarkedUnread", + "mail.read": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailRead", + "mail.replied": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailReplied", + "mail.sent": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailSent", + "order.completed": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderCompleted", + "order.failed": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderFailed", + "order.fired": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderFired", + "provider.swapped": "#/components/schemas/TypedTaggedEventStreamEnvelopeProviderSwapped", + "session.crashed": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionCrashed", + "session.draining": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionDraining", + "session.idle_killed": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionIdleKilled", + "session.quarantined": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionQuarantined", + "session.stopped": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionStopped", + "session.suspended": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionSuspended", + "session.undrained": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionUndrained", + "session.updated": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionUpdated", + "session.woke": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionWoke", + "worker.operation": "#/components/schemas/TypedTaggedEventStreamEnvelopeWorkerOperation" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadClosed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadCreated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadUpdated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityCreated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityInitFailed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityReady" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityResumed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCitySuspended" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregisterFailed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregisterRequested" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregistered" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeControllerStarted" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeControllerStopped" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeConvoyClosed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeConvoyCreated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgBound" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgGroupCreated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgInbound" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgOutbound" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgUnbound" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailArchived" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailDeleted" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailMarkedRead" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailMarkedUnread" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailRead" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailReplied" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailSent" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderCompleted" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderFailed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderFired" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeProviderSwapped" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionCrashed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionDraining" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionIdleKilled" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionQuarantined" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionStopped" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionSuspended" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionUndrained" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionUpdated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionWoke" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeWorkerOperation" + } + ], + "title": "Typed supervisor event stream envelope" + }, + "TypedTaggedEventStreamEnvelopeBeadClosed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BeadEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "bead.closed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope bead.closed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeBeadCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BeadEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "bead.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope bead.created", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeBeadUpdated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BeadEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "bead.updated", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope bead.updated", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.created", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityInitFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.init_failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.init_failed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityReady": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.ready", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.ready", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityResumed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.resumed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.resumed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCitySuspended": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.suspended", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.suspended", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityUnregisterFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregister_failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.unregister_failed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityUnregisterRequested": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregister_requested", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.unregister_requested", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityUnregistered": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregistered", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.unregistered", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeControllerStarted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "controller.started", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope controller.started", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeControllerStopped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "controller.stopped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope controller.stopped", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeConvoyClosed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "convoy.closed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope convoy.closed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeConvoyCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "convoy.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope convoy.created", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/AdapterEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.adapter_added", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.adapter_added", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/AdapterEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.adapter_removed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.adapter_removed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgBound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BoundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.bound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.bound", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgGroupCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/GroupCreatedEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.group_created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.group_created", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgInbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/InboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.inbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.inbound", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgOutbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/OutboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.outbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.outbound", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgUnbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/UnboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.unbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.unbound", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailArchived": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.archived", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.archived", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailDeleted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.deleted", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.deleted", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailMarkedRead": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.marked_read", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.marked_read", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailMarkedUnread": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.marked_unread", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.marked_unread", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailRead": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.read", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.read", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailReplied": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.replied", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.replied", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailSent": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.sent", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.sent", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeOrderCompleted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.completed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope order.completed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeOrderFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope order.failed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeOrderFired": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.fired", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope order.fired", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeProviderSwapped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "provider.swapped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope provider.swapped", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionCrashed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.crashed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.crashed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionDraining": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.draining", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.draining", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionIdleKilled": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.idle_killed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.idle_killed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionQuarantined": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.quarantined", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.quarantined", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionStopped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.stopped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.stopped", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionSuspended": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.suspended", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.suspended", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionUndrained": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.undrained", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.undrained", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionUpdated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.updated", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.updated", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionWoke": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.woke", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.woke", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeWorkerOperation": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/WorkerOperationEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "worker.operation", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope worker.operation", + "type": "object" + }, + "UnboundEventPayload": { + "additionalProperties": false, + "properties": { + "count": { + "format": "int64", + "type": "integer" + }, + "session_id": { + "type": "string" + } + }, + "required": [ + "session_id", + "count" + ], + "type": "object" + }, + "WireEvent": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/EventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor" + ], + "type": "object" + }, + "WireTaggedEvent": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/EventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "city", + "seq", + "type", + "ts", + "actor" + ], + "type": "object" + }, + "WorkerOperationEventPayload": { + "additionalProperties": false, + "properties": { + "delivered": { + "type": "boolean" }, "duration_ms": { "format": "int64", @@ -6852,7 +11184,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -6862,7 +11199,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get health" @@ -6880,7 +11222,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -6890,7 +11237,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 cities" @@ -6899,6 +11251,19 @@ "/v0/city": { "post": { "operationId": "post-v0-city", + "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + } + ], "requestBody": { "content": { "application/json": { @@ -6910,7 +11275,7 @@ "required": true }, "responses": { - "200": { + "202": { "content": { "application/json": { "schema": { @@ -6918,7 +11283,12 @@ } } }, - "description": "OK" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -6928,7 +11298,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city" @@ -6960,7 +11335,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -6970,7 +11350,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name" @@ -6978,6 +11363,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7010,7 +11406,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7020,7 +11421,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name" @@ -7030,6 +11436,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-agent-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7062,7 +11479,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7072,7 +11494,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name agent by base" @@ -7121,6 +11548,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -7132,7 +11562,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agent by base" @@ -7140,6 +11575,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-agent-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7182,7 +11628,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7192,7 +11643,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name agent by base" @@ -7254,7 +11710,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7264,7 +11725,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agent by base output" @@ -7365,7 +11831,19 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "GC-Agent-Status": { + "description": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "schema": { + "description": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "type": "string" + } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7375,7 +11853,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream agent output in real time" @@ -7385,6 +11868,17 @@ "post": { "operationId": "post-v0-city-by-city-name-agent-by-base-by-action", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7431,7 +11925,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7441,7 +11940,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name agent by base by action" @@ -7451,6 +11955,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-agent-by-dir-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7493,7 +12008,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7503,7 +12023,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name agent by dir by base" @@ -7562,6 +12087,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -7573,7 +12101,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agent by dir by base" @@ -7581,6 +12114,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-agent-by-dir-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7633,7 +12177,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7643,7 +12192,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name agent by dir by base" @@ -7715,7 +12269,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7725,7 +12284,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agent by dir by base output" @@ -7836,7 +12400,19 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "GC-Agent-Status": { + "description": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "schema": { + "description": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "type": "string" + } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7846,7 +12422,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream agent output in real time (qualified name)" @@ -7856,6 +12437,17 @@ "post": { "operationId": "post-v0-city-by-city-name-agent-by-dir-by-base-by-action", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7912,7 +12504,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7922,7 +12519,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name agent by dir by base by action" @@ -8027,6 +12629,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8038,7 +12643,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agents" @@ -8046,6 +12656,17 @@ "post": { "operationId": "create-agent", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8078,7 +12699,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8088,7 +12714,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create an agent" @@ -8098,6 +12729,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-bead-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8130,7 +12772,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8140,7 +12787,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name bead by ID" @@ -8189,6 +12841,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8200,7 +12855,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name bead by ID" @@ -8208,6 +12868,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-bead-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8250,7 +12921,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8260,7 +12936,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name bead by ID" @@ -8270,6 +12951,17 @@ "post": { "operationId": "post-v0-city-by-city-name-bead-by-id-assign", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8324,6 +13016,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8335,7 +13030,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name bead by ID assign" @@ -8345,6 +13045,17 @@ "post": { "operationId": "post-v0-city-by-city-name-bead-by-id-close", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8377,7 +13088,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8387,7 +13103,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name bead by ID close" @@ -8438,6 +13159,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8449,7 +13173,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name bead by ID deps" @@ -8459,6 +13188,17 @@ "post": { "operationId": "post-v0-city-by-city-name-bead-by-id-reopen", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8491,7 +13231,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8501,7 +13246,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name bead by ID reopen" @@ -8511,6 +13261,17 @@ "post": { "operationId": "post-v0-city-by-city-name-bead-by-id-update", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8553,7 +13314,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8563,7 +13329,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name bead by ID update" @@ -8696,6 +13467,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8707,7 +13481,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name beads" @@ -8715,6 +13494,17 @@ "post": { "operationId": "create-bead", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8765,6 +13555,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8776,7 +13569,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a bead" @@ -8827,6 +13625,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8838,7 +13639,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name beads graph by root ID" @@ -8899,6 +13705,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8910,7 +13719,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name beads ready" @@ -8951,6 +13765,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8962,7 +13779,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name config" @@ -9003,6 +13825,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9014,7 +13839,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name config explain" @@ -9046,7 +13876,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9056,7 +13891,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name config validate" @@ -9066,6 +13906,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-convoy-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9098,7 +13949,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9108,7 +13964,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name convoy by ID" @@ -9157,6 +14018,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9168,7 +14032,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name convoy by ID" @@ -9178,6 +14047,17 @@ "post": { "operationId": "post-v0-city-by-city-name-convoy-by-id-add", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9220,7 +14100,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9230,7 +14115,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name convoy by ID add" @@ -9281,6 +14171,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9292,7 +14185,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name convoy by ID check" @@ -9302,6 +14200,17 @@ "post": { "operationId": "post-v0-city-by-city-name-convoy-by-id-close", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9334,7 +14243,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9344,7 +14258,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name convoy by ID close" @@ -9354,6 +14273,17 @@ "post": { "operationId": "post-v0-city-by-city-name-convoy-by-id-remove", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9396,7 +14326,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9406,7 +14341,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name convoy by ID remove" @@ -9489,6 +14429,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9500,7 +14443,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name convoys" @@ -9508,6 +14456,17 @@ "post": { "operationId": "create-convoy", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9549,6 +14508,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9560,7 +14522,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a convoy" @@ -9673,6 +14640,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9684,7 +14654,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name events" @@ -9692,6 +14667,17 @@ "post": { "operationId": "emit-event", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9724,7 +14710,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9734,7 +14725,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Emit an event" @@ -9788,7 +14784,7 @@ { "properties": { "data": { - "$ref": "#/components/schemas/EventStreamEnvelope" + "$ref": "#/components/schemas/TypedEventStreamEnvelope" }, "event": { "const": "event", @@ -9844,7 +14840,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9854,7 +14855,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream city events in real time" @@ -9864,6 +14870,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-extmsg-adapters", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9896,7 +14913,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9906,7 +14928,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name extmsg adapters" @@ -9945,6 +14972,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9956,7 +14986,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name extmsg adapters" @@ -9964,6 +14999,17 @@ "post": { "operationId": "register-extmsg-adapter", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9996,7 +15042,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10006,7 +15057,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Register an external messaging adapter" @@ -10016,6 +15072,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-bind", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10048,7 +15115,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10058,7 +15130,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg bind" @@ -10109,6 +15186,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -10120,7 +15200,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name extmsg bindings" @@ -10202,7 +15287,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10212,7 +15302,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name extmsg groups" @@ -10220,6 +15315,17 @@ "post": { "operationId": "ensure-extmsg-group", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10252,7 +15358,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10262,7 +15373,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Ensure an external messaging group exists" @@ -10272,6 +15388,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-inbound", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10304,7 +15431,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10314,7 +15446,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg inbound" @@ -10324,6 +15461,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-outbound", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10356,7 +15504,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10366,7 +15519,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg outbound" @@ -10376,6 +15534,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-extmsg-participants", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10408,7 +15577,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10418,7 +15592,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name extmsg participants" @@ -10426,6 +15605,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-participants", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10458,7 +15648,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10468,7 +15663,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg participants" @@ -10569,6 +15769,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -10580,7 +15783,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name extmsg transcript" @@ -10590,6 +15798,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-transcript-ack", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10622,7 +15841,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10632,7 +15856,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg transcript ack" @@ -10642,6 +15871,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-unbind", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10674,7 +15914,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10684,7 +15929,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg unbind" @@ -10757,7 +16007,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10767,7 +16022,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formula by name" @@ -10819,7 +16079,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10829,7 +16094,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formulas" @@ -10893,7 +16163,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10903,7 +16178,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formulas feed" @@ -10976,7 +16256,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10986,7 +16271,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formulas by name" @@ -10996,6 +16286,17 @@ "post": { "operationId": "post-v0-city-by-city-name-formulas-by-name-preview", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11038,7 +16339,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11048,7 +16354,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name formulas by name preview" @@ -11124,7 +16435,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11134,7 +16450,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formulas by name runs" @@ -11166,7 +16487,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11176,7 +16502,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name health" @@ -11289,6 +16620,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11300,7 +16634,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name mail" @@ -11308,6 +16647,17 @@ "post": { "operationId": "send-mail", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11358,6 +16708,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11369,7 +16722,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Send a mail message" @@ -11421,7 +16779,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11431,7 +16794,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name mail count" @@ -11492,6 +16860,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11503,7 +16874,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name mail thread by ID" @@ -11513,6 +16889,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-mail-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11555,7 +16942,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11565,7 +16957,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name mail by ID" @@ -11624,6 +17021,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11635,7 +17035,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name mail by ID" @@ -11645,6 +17050,17 @@ "post": { "operationId": "post-v0-city-by-city-name-mail-by-id-archive", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11687,7 +17103,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11697,7 +17118,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name mail by ID archive" @@ -11707,6 +17133,17 @@ "post": { "operationId": "post-v0-city-by-city-name-mail-by-id-mark-unread", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11749,7 +17186,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11759,7 +17201,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name mail by ID mark unread" @@ -11769,6 +17216,17 @@ "post": { "operationId": "post-v0-city-by-city-name-mail-by-id-read", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11811,7 +17269,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11821,7 +17284,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name mail by ID read" @@ -11831,6 +17299,17 @@ "post": { "operationId": "reply-mail", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11892,6 +17371,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11903,7 +17385,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Reply to a mail message" @@ -11955,7 +17442,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11965,7 +17457,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name order history by bead ID" @@ -12007,7 +17504,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12017,7 +17519,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name order by name" @@ -12027,6 +17534,17 @@ "post": { "operationId": "post-v0-city-by-city-name-order-by-name-disable", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12059,7 +17577,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12069,7 +17592,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name order by name disable" @@ -12079,6 +17607,17 @@ "post": { "operationId": "post-v0-city-by-city-name-order-by-name-enable", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12111,7 +17650,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12121,7 +17665,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name order by name enable" @@ -12153,7 +17702,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12163,7 +17717,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name orders" @@ -12195,7 +17754,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12205,7 +17769,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name orders check" @@ -12269,7 +17838,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12279,7 +17853,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name orders feed" @@ -12345,7 +17924,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12355,7 +17939,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name orders history" @@ -12387,7 +17976,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12397,7 +17991,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name packs" @@ -12407,6 +18006,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-patches-agent-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12439,7 +18049,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12449,7 +18064,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name patches agent by base" @@ -12498,6 +18118,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12509,7 +18132,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches agent by base" @@ -12519,6 +18147,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-patches-agent-by-dir-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12561,7 +18200,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12571,7 +18215,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name patches agent by dir by base" @@ -12630,6 +18279,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12641,7 +18293,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches agent by dir by base" @@ -12682,6 +18339,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12693,7 +18353,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches agents" @@ -12701,6 +18366,17 @@ "put": { "operationId": "put-v0-city-by-city-name-patches-agents", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12733,7 +18409,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12743,7 +18424,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Put v0 city by city name patches agents" @@ -12753,6 +18439,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-patches-provider-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12785,7 +18482,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12795,7 +18497,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name patches provider by name" @@ -12844,6 +18551,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12855,7 +18565,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches provider by name" @@ -12896,6 +18611,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12907,7 +18625,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches providers" @@ -12915,6 +18638,17 @@ "put": { "operationId": "put-v0-city-by-city-name-patches-providers", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12947,7 +18681,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12957,7 +18696,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Put v0 city by city name patches providers" @@ -12967,6 +18711,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-patches-rig-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12999,7 +18754,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13009,7 +18769,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name patches rig by name" @@ -13058,6 +18823,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13069,7 +18837,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches rig by name" @@ -13110,6 +18883,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13121,7 +18897,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches rigs" @@ -13129,6 +18910,17 @@ "put": { "operationId": "put-v0-city-by-city-name-patches-rigs", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13161,7 +18953,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13171,7 +18968,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Put v0 city by city name patches rigs" @@ -13223,7 +19025,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13233,7 +19040,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name provider readiness" @@ -13243,6 +19055,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-provider-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13275,7 +19098,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13285,7 +19113,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name provider by name" @@ -13334,6 +19167,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13345,7 +19181,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name provider by name" @@ -13353,6 +19194,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-provider-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13395,7 +19247,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13405,7 +19262,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name provider by name" @@ -13446,6 +19308,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13457,7 +19322,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name providers" @@ -13465,6 +19335,17 @@ "post": { "operationId": "create-provider", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13497,7 +19378,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13507,7 +19393,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a provider" @@ -13548,6 +19439,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13559,7 +19453,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name providers public" @@ -13611,7 +19510,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13621,7 +19525,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name readiness" @@ -13631,6 +19540,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-rig-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13663,7 +19583,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13673,7 +19598,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name rig by name" @@ -13732,6 +19662,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13743,7 +19676,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name rig by name" @@ -13751,6 +19689,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-rig-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13793,7 +19742,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13803,7 +19757,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name rig by name" @@ -13813,6 +19772,17 @@ "post": { "operationId": "post-v0-city-by-city-name-rig-by-name-by-action", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13855,7 +19825,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13865,7 +19840,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name rig by name by action" @@ -13936,6 +19916,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13947,7 +19930,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name rigs" @@ -13955,6 +19943,17 @@ "post": { "operationId": "create-rig", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13987,7 +19986,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13997,7 +20001,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a rig" @@ -14048,6 +20057,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14059,7 +20071,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name service by name" @@ -14069,6 +20086,17 @@ "post": { "operationId": "post-v0-city-by-city-name-service-by-name-restart", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14101,7 +20129,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14111,7 +20144,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name service by name restart" @@ -14152,6 +20190,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14163,7 +20204,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name services" @@ -14224,6 +20270,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14235,7 +20284,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID" @@ -14243,6 +20297,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-session-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14294,6 +20359,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14305,7 +20373,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name session by ID" @@ -14356,6 +20429,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14367,7 +20443,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID agents" @@ -14428,6 +20509,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14439,7 +20523,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID agents by agent ID" @@ -14449,6 +20538,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-close", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14491,7 +20591,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14501,7 +20606,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID close" @@ -14511,6 +20621,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-kill", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14543,7 +20664,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14553,7 +20679,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID kill" @@ -14563,6 +20694,17 @@ "post": { "operationId": "send-session-message", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14605,7 +20747,12 @@ } } }, - "description": "Accepted" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14615,7 +20762,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Send a message to a session" @@ -14666,6 +20818,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14677,7 +20832,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID pending" @@ -14687,6 +20847,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-rename", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14738,6 +20909,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14749,7 +20923,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID rename" @@ -14759,6 +20938,17 @@ "post": { "operationId": "respond-session", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14801,7 +20991,12 @@ } } }, - "description": "Accepted" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14811,7 +21006,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Respond to a pending interaction" @@ -14821,6 +21021,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-stop", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14853,7 +21064,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14863,7 +21079,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID stop" @@ -15051,7 +21272,26 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "GC-Session-State": { + "description": "Session state at the time streaming began (e.g. active, closed).", + "schema": { + "description": "Session state at the time streaming began (e.g. active, closed).", + "type": "string" + } + }, + "GC-Session-Status": { + "description": "Runtime status at the time streaming began. Emitted as \"stopped\" when the session's underlying process is not running.", + "schema": { + "description": "Runtime status at the time streaming began. Emitted as \"stopped\" when the session's underlying process is not running.", + "type": "string" + } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15061,7 +21301,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream session output in real time" @@ -15071,6 +21316,17 @@ "post": { "operationId": "submit-session", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15113,7 +21369,12 @@ } } }, - "description": "Accepted" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15123,7 +21384,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Submit a message to a session" @@ -15133,6 +21399,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-suspend", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15165,7 +21442,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15175,7 +21457,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID suspend" @@ -15256,6 +21543,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -15267,7 +21557,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID transcript" @@ -15277,6 +21572,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-wake", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15309,7 +21615,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15319,7 +21630,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID wake" @@ -15412,6 +21728,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -15423,7 +21742,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name sessions" @@ -15431,6 +21755,17 @@ "post": { "operationId": "create-session", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15463,7 +21798,12 @@ } } }, - "description": "Accepted" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15473,7 +21813,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a session" @@ -15483,6 +21828,17 @@ "post": { "operationId": "post-v0-city-by-city-name-sling", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15515,7 +21871,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15525,7 +21886,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name sling" @@ -15586,6 +21952,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -15597,16 +21966,93 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name status" } }, + "/v0/city/{cityName}/unregister": { + "post": { + "operationId": "post-v0-city-by-city-name-unregister", + "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, + { + "description": "Supervisor-registered city name.", + "in": "path", + "name": "cityName", + "required": true, + "schema": { + "description": "Supervisor-registered city name.", + "type": "string" + } + } + ], + "responses": { + "202": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CityUnregisterResponse" + } + } + }, + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } + } + }, + "summary": "Post v0 city by city name unregister" + } + }, "/v0/city/{cityName}/workflow/{workflow_id}": { "delete": { "operationId": "delete-v0-city-by-city-name-workflow-by-workflow-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15669,7 +22115,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15679,7 +22130,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name workflow by workflow ID" @@ -15748,6 +22204,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -15759,7 +22218,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name workflow by workflow ID" @@ -15821,7 +22285,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15831,7 +22300,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 events" @@ -15898,7 +22372,7 @@ { "properties": { "data": { - "$ref": "#/components/schemas/TaggedEventStreamEnvelope" + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelope" }, "event": { "const": "tagged_event", @@ -15928,7 +22402,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15938,7 +22417,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream tagged events from all running cities." @@ -15978,7 +22462,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15988,7 +22477,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 provider readiness" @@ -16028,7 +22522,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -16038,7 +22537,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 readiness" diff --git a/docs/schema/openapi.txt b/docs/schema/openapi.txt index ee1f1e44e7..dcac755149 100644 --- a/docs/schema/openapi.txt +++ b/docs/schema/openapi.txt @@ -1,5 +1,14 @@ { "components": { + "headers": { + "X-GC-Request-Id": { + "description": "Opaque per-response identifier assigned by the server for log correlation. Every response carries this header.", + "schema": { + "description": "Opaque per-response identifier assigned by the server for log correlation. Every response carries this header.", + "type": "string" + } + } + }, "schemas": { "AdapterCapabilities": { "additionalProperties": false, @@ -665,6 +674,15 @@ "AnnotatedProviderResponse": { "additionalProperties": false, "properties": { + "acp_args": { + "items": { + "type": "string" + }, + "type": "array" + }, + "acp_command": { + "type": "string" + }, "args": { "items": { "type": "string" @@ -818,6 +836,17 @@ "null" ] }, + "metadata": { + "additionalProperties": { + "type": "string" + }, + "description": "Metadata key-value pairs to set at create time.", + "type": "object" + }, + "parent": { + "description": "Parent bead ID.", + "type": "string" + }, "priority": { "description": "Bead priority.", "format": "int64", @@ -932,6 +961,13 @@ "description": "Metadata key-value pairs to set.", "type": "object" }, + "parent": { + "description": "Parent bead ID. Use null or an empty string to clear.", + "type": [ + "string", + "null" + ] + }, "priority": { "description": "Bead priority.", "format": "int64", @@ -1023,17 +1059,22 @@ "CityCreateResponse": { "additionalProperties": false, "properties": { + "name": { + "description": "Resolved city name as persisted in city.toml. Use this to filter the event stream for completion.", + "type": "string" + }, "ok": { - "description": "True on success.", + "description": "True when scaffolding + registration succeeded. Does not imply the city is ready yet; watch /v0/events/stream for city.ready.", "type": "boolean" }, "path": { - "description": "Resolved absolute path of the created city.", + "description": "Resolved absolute path of the created city directory.", "type": "string" } }, "required": [ "ok", + "name", "path" ], "type": "object" @@ -1117,6 +1158,34 @@ ], "type": "object" }, + "CityLifecyclePayload": { + "additionalProperties": false, + "properties": { + "error": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "phases_completed": { + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + "required": [ + "name", + "path" + ], + "type": "object" + }, "CityPatchInputBody": { "additionalProperties": false, "properties": { @@ -1127,6 +1196,29 @@ }, "type": "object" }, + "CityUnregisterResponse": { + "additionalProperties": false, + "properties": { + "name": { + "description": "Resolved registry name. Filter the event stream by this to observe completion.", + "type": "string" + }, + "ok": { + "description": "True when the registry entry was removed and the supervisor was signaled. Does not imply the city's controller has stopped yet; watch /v0/events/stream for city.unregistered.", + "type": "boolean" + }, + "path": { + "description": "Resolved absolute city directory. The directory itself is not modified; unregister only affects the supervisor's registry.", + "type": "string" + } + }, + "required": [ + "ok", + "name", + "path" + ], + "type": "object" + }, "ConfigAgentResponse": { "additionalProperties": false, "properties": { @@ -1796,10 +1888,16 @@ "default": "about:blank", "description": "A URI reference to human-readable documentation for the error.", "examples": [ - "https://example.com/errors/example" + "https://example.com/errors/example", + "urn:gascity:error:sling-missing-bead", + "urn:gascity:error:sling-cross-rig" ], "format": "uri", - "type": "string" + "type": "string", + "x-gascity-problem-types": [ + "urn:gascity:error:sling-missing-bead", + "urn:gascity:error:sling-cross-rig" + ] } }, "type": "object" @@ -1859,6 +1957,9 @@ { "$ref": "#/components/schemas/BoundEventPayload" }, + { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, { "$ref": "#/components/schemas/GroupCreatedEventPayload" }, @@ -4381,6 +4482,20 @@ "ProviderCreateInputBody": { "additionalProperties": false, "properties": { + "acp_args": { + "description": "ACP transport command arguments override.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "acp_command": { + "description": "ACP transport command binary override.", + "type": "string" + }, "args": { "description": "Command arguments.", "items": { @@ -4506,6 +4621,21 @@ "ProviderPatch": { "additionalProperties": false, "properties": { + "ACPArgs": { + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "ACPCommand": { + "type": [ + "string", + "null" + ] + }, "Args": { "items": { "type": "string" @@ -4587,7 +4717,9 @@ "Name", "Base", "Command", + "ACPCommand", "Args", + "ACPArgs", "ArgsAppend", "OptionsSchemaMerge", "PromptMode", @@ -4602,6 +4734,20 @@ "ProviderPatchSetInputBody": { "additionalProperties": false, "properties": { + "acp_args": { + "description": "Override ACP transport command arguments.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "acp_command": { + "description": "Override ACP transport command binary.", + "type": "string" + }, "args": { "description": "Override command arguments.", "items": { @@ -4747,6 +4893,15 @@ "ProviderResponse": { "additionalProperties": false, "properties": { + "acp_args": { + "items": { + "type": "string" + }, + "type": "array" + }, + "acp_command": { + "type": "string" + }, "args": { "items": { "type": "string" @@ -4798,6 +4953,15 @@ "ProviderSpecJSON": { "additionalProperties": false, "properties": { + "acp_args": { + "items": { + "type": "string" + }, + "type": "array" + }, + "acp_command": { + "type": "string" + }, "args": { "items": { "type": "string" @@ -4835,6 +4999,20 @@ "ProviderUpdateInputBody": { "additionalProperties": false, "properties": { + "acp_args": { + "description": "ACP transport command arguments override.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "acp_command": { + "description": "ACP transport command binary override.", + "type": "string" + }, "args": { "description": "Command arguments.", "items": { @@ -5825,6 +6003,10 @@ "description": "Bead ID to sling.", "type": "string" }, + "force": { + "description": "Bypass cross-rig guards; for direct bead routes, also bypass missing-bead validation. Formula-backed graph routes may replace existing live workflow roots but still require the source bead to exist.", + "type": "boolean" + }, "formula": { "description": "Formula name for workflow launch.", "type": "string" @@ -6335,24 +6517,190 @@ ], "type": "string" }, - "UnboundEventPayload": { - "additionalProperties": false, - "properties": { - "count": { - "format": "int64", - "type": "integer" + "TypedEventStreamEnvelope": { + "description": "Discriminated union of city event stream envelopes. Each variant constrains the envelope type and payload schema together.", + "discriminator": { + "mapping": { + "bead.closed": "#/components/schemas/TypedEventStreamEnvelopeBeadClosed", + "bead.created": "#/components/schemas/TypedEventStreamEnvelopeBeadCreated", + "bead.updated": "#/components/schemas/TypedEventStreamEnvelopeBeadUpdated", + "city.created": "#/components/schemas/TypedEventStreamEnvelopeCityCreated", + "city.init_failed": "#/components/schemas/TypedEventStreamEnvelopeCityInitFailed", + "city.ready": "#/components/schemas/TypedEventStreamEnvelopeCityReady", + "city.resumed": "#/components/schemas/TypedEventStreamEnvelopeCityResumed", + "city.suspended": "#/components/schemas/TypedEventStreamEnvelopeCitySuspended", + "city.unregister_failed": "#/components/schemas/TypedEventStreamEnvelopeCityUnregisterFailed", + "city.unregister_requested": "#/components/schemas/TypedEventStreamEnvelopeCityUnregisterRequested", + "city.unregistered": "#/components/schemas/TypedEventStreamEnvelopeCityUnregistered", + "controller.started": "#/components/schemas/TypedEventStreamEnvelopeControllerStarted", + "controller.stopped": "#/components/schemas/TypedEventStreamEnvelopeControllerStopped", + "convoy.closed": "#/components/schemas/TypedEventStreamEnvelopeConvoyClosed", + "convoy.created": "#/components/schemas/TypedEventStreamEnvelopeConvoyCreated", + "extmsg.adapter_added": "#/components/schemas/TypedEventStreamEnvelopeExtmsgAdapterAdded", + "extmsg.adapter_removed": "#/components/schemas/TypedEventStreamEnvelopeExtmsgAdapterRemoved", + "extmsg.bound": "#/components/schemas/TypedEventStreamEnvelopeExtmsgBound", + "extmsg.group_created": "#/components/schemas/TypedEventStreamEnvelopeExtmsgGroupCreated", + "extmsg.inbound": "#/components/schemas/TypedEventStreamEnvelopeExtmsgInbound", + "extmsg.outbound": "#/components/schemas/TypedEventStreamEnvelopeExtmsgOutbound", + "extmsg.unbound": "#/components/schemas/TypedEventStreamEnvelopeExtmsgUnbound", + "mail.archived": "#/components/schemas/TypedEventStreamEnvelopeMailArchived", + "mail.deleted": "#/components/schemas/TypedEventStreamEnvelopeMailDeleted", + "mail.marked_read": "#/components/schemas/TypedEventStreamEnvelopeMailMarkedRead", + "mail.marked_unread": "#/components/schemas/TypedEventStreamEnvelopeMailMarkedUnread", + "mail.read": "#/components/schemas/TypedEventStreamEnvelopeMailRead", + "mail.replied": "#/components/schemas/TypedEventStreamEnvelopeMailReplied", + "mail.sent": "#/components/schemas/TypedEventStreamEnvelopeMailSent", + "order.completed": "#/components/schemas/TypedEventStreamEnvelopeOrderCompleted", + "order.failed": "#/components/schemas/TypedEventStreamEnvelopeOrderFailed", + "order.fired": "#/components/schemas/TypedEventStreamEnvelopeOrderFired", + "provider.swapped": "#/components/schemas/TypedEventStreamEnvelopeProviderSwapped", + "session.crashed": "#/components/schemas/TypedEventStreamEnvelopeSessionCrashed", + "session.draining": "#/components/schemas/TypedEventStreamEnvelopeSessionDraining", + "session.idle_killed": "#/components/schemas/TypedEventStreamEnvelopeSessionIdleKilled", + "session.quarantined": "#/components/schemas/TypedEventStreamEnvelopeSessionQuarantined", + "session.stopped": "#/components/schemas/TypedEventStreamEnvelopeSessionStopped", + "session.suspended": "#/components/schemas/TypedEventStreamEnvelopeSessionSuspended", + "session.undrained": "#/components/schemas/TypedEventStreamEnvelopeSessionUndrained", + "session.updated": "#/components/schemas/TypedEventStreamEnvelopeSessionUpdated", + "session.woke": "#/components/schemas/TypedEventStreamEnvelopeSessionWoke", + "worker.operation": "#/components/schemas/TypedEventStreamEnvelopeWorkerOperation" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeBeadClosed" }, - "session_id": { - "type": "string" + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeBeadCreated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeBeadUpdated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityCreated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityInitFailed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityReady" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityResumed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCitySuspended" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityUnregisterFailed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityUnregisterRequested" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityUnregistered" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeControllerStarted" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeControllerStopped" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeConvoyClosed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeConvoyCreated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgAdapterAdded" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgAdapterRemoved" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgBound" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgGroupCreated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgInbound" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgOutbound" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgUnbound" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailArchived" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailDeleted" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailMarkedRead" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailMarkedUnread" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailRead" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailReplied" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailSent" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeOrderCompleted" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeOrderFailed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeOrderFired" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeProviderSwapped" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionCrashed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionDraining" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionIdleKilled" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionQuarantined" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionStopped" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionSuspended" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionUndrained" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionUpdated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionWoke" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeWorkerOperation" } - }, - "required": [ - "session_id", - "count" ], - "type": "object" + "title": "Typed city event stream envelope" }, - "WireEvent": { + "TypedEventStreamEnvelopeBeadClosed": { "additionalProperties": false, "properties": { "actor": { @@ -6362,7 +6710,7 @@ "type": "string" }, "payload": { - "$ref": "#/components/schemas/EventPayload" + "$ref": "#/components/schemas/BeadEventPayload" }, "seq": { "format": "int64", @@ -6377,31 +6725,34 @@ "type": "string" }, "type": { + "const": "bead.closed", "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" } }, "required": [ "seq", "type", "ts", - "actor" + "actor", + "payload" ], + "title": "TypedEventStreamEnvelope bead.closed", "type": "object" }, - "WireTaggedEvent": { + "TypedEventStreamEnvelopeBeadCreated": { "additionalProperties": false, "properties": { "actor": { "type": "string" }, - "city": { - "type": "string" - }, "message": { "type": "string" }, "payload": { - "$ref": "#/components/schemas/EventPayload" + "$ref": "#/components/schemas/BeadEventPayload" }, "seq": { "format": "int64", @@ -6416,23 +6767,4004 @@ "type": "string" }, "type": { + "const": "bead.created", "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" } }, "required": [ - "city", "seq", "type", "ts", - "actor" + "actor", + "payload" ], + "title": "TypedEventStreamEnvelope bead.created", "type": "object" }, - "WorkerOperationEventPayload": { + "TypedEventStreamEnvelopeBeadUpdated": { "additionalProperties": false, "properties": { - "delivered": { - "type": "boolean" + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BeadEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "bead.updated", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope bead.updated", + "type": "object" + }, + "TypedEventStreamEnvelopeCityCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.created", + "type": "object" + }, + "TypedEventStreamEnvelopeCityInitFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.init_failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.init_failed", + "type": "object" + }, + "TypedEventStreamEnvelopeCityReady": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.ready", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.ready", + "type": "object" + }, + "TypedEventStreamEnvelopeCityResumed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.resumed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.resumed", + "type": "object" + }, + "TypedEventStreamEnvelopeCitySuspended": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.suspended", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.suspended", + "type": "object" + }, + "TypedEventStreamEnvelopeCityUnregisterFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregister_failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.unregister_failed", + "type": "object" + }, + "TypedEventStreamEnvelopeCityUnregisterRequested": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregister_requested", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.unregister_requested", + "type": "object" + }, + "TypedEventStreamEnvelopeCityUnregistered": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregistered", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.unregistered", + "type": "object" + }, + "TypedEventStreamEnvelopeControllerStarted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "controller.started", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope controller.started", + "type": "object" + }, + "TypedEventStreamEnvelopeControllerStopped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "controller.stopped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope controller.stopped", + "type": "object" + }, + "TypedEventStreamEnvelopeConvoyClosed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "convoy.closed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope convoy.closed", + "type": "object" + }, + "TypedEventStreamEnvelopeConvoyCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "convoy.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope convoy.created", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgAdapterAdded": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/AdapterEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.adapter_added", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.adapter_added", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgAdapterRemoved": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/AdapterEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.adapter_removed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.adapter_removed", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgBound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BoundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.bound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.bound", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgGroupCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/GroupCreatedEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.group_created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.group_created", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgInbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/InboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.inbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.inbound", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgOutbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/OutboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.outbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.outbound", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgUnbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/UnboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.unbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.unbound", + "type": "object" + }, + "TypedEventStreamEnvelopeMailArchived": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.archived", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.archived", + "type": "object" + }, + "TypedEventStreamEnvelopeMailDeleted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.deleted", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.deleted", + "type": "object" + }, + "TypedEventStreamEnvelopeMailMarkedRead": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.marked_read", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.marked_read", + "type": "object" + }, + "TypedEventStreamEnvelopeMailMarkedUnread": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.marked_unread", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.marked_unread", + "type": "object" + }, + "TypedEventStreamEnvelopeMailRead": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.read", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.read", + "type": "object" + }, + "TypedEventStreamEnvelopeMailReplied": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.replied", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.replied", + "type": "object" + }, + "TypedEventStreamEnvelopeMailSent": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.sent", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.sent", + "type": "object" + }, + "TypedEventStreamEnvelopeOrderCompleted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.completed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope order.completed", + "type": "object" + }, + "TypedEventStreamEnvelopeOrderFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope order.failed", + "type": "object" + }, + "TypedEventStreamEnvelopeOrderFired": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.fired", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope order.fired", + "type": "object" + }, + "TypedEventStreamEnvelopeProviderSwapped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "provider.swapped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope provider.swapped", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionCrashed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.crashed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.crashed", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionDraining": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.draining", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.draining", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionIdleKilled": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.idle_killed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.idle_killed", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionQuarantined": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.quarantined", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.quarantined", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionStopped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.stopped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.stopped", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionSuspended": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.suspended", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.suspended", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionUndrained": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.undrained", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.undrained", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionUpdated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.updated", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.updated", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionWoke": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.woke", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.woke", + "type": "object" + }, + "TypedEventStreamEnvelopeWorkerOperation": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/WorkerOperationEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "worker.operation", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope worker.operation", + "type": "object" + }, + "TypedTaggedEventStreamEnvelope": { + "description": "Discriminated union of supervisor event stream envelopes. Each variant constrains the envelope type and payload schema together and includes the source city.", + "discriminator": { + "mapping": { + "bead.closed": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadClosed", + "bead.created": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadCreated", + "bead.updated": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadUpdated", + "city.created": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityCreated", + "city.init_failed": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityInitFailed", + "city.ready": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityReady", + "city.resumed": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityResumed", + "city.suspended": "#/components/schemas/TypedTaggedEventStreamEnvelopeCitySuspended", + "city.unregister_failed": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregisterFailed", + "city.unregister_requested": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregisterRequested", + "city.unregistered": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregistered", + "controller.started": "#/components/schemas/TypedTaggedEventStreamEnvelopeControllerStarted", + "controller.stopped": "#/components/schemas/TypedTaggedEventStreamEnvelopeControllerStopped", + "convoy.closed": "#/components/schemas/TypedTaggedEventStreamEnvelopeConvoyClosed", + "convoy.created": "#/components/schemas/TypedTaggedEventStreamEnvelopeConvoyCreated", + "extmsg.adapter_added": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded", + "extmsg.adapter_removed": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved", + "extmsg.bound": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgBound", + "extmsg.group_created": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgGroupCreated", + "extmsg.inbound": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgInbound", + "extmsg.outbound": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgOutbound", + "extmsg.unbound": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgUnbound", + "mail.archived": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailArchived", + "mail.deleted": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailDeleted", + "mail.marked_read": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailMarkedRead", + "mail.marked_unread": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailMarkedUnread", + "mail.read": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailRead", + "mail.replied": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailReplied", + "mail.sent": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailSent", + "order.completed": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderCompleted", + "order.failed": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderFailed", + "order.fired": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderFired", + "provider.swapped": "#/components/schemas/TypedTaggedEventStreamEnvelopeProviderSwapped", + "session.crashed": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionCrashed", + "session.draining": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionDraining", + "session.idle_killed": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionIdleKilled", + "session.quarantined": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionQuarantined", + "session.stopped": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionStopped", + "session.suspended": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionSuspended", + "session.undrained": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionUndrained", + "session.updated": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionUpdated", + "session.woke": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionWoke", + "worker.operation": "#/components/schemas/TypedTaggedEventStreamEnvelopeWorkerOperation" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadClosed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadCreated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadUpdated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityCreated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityInitFailed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityReady" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityResumed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCitySuspended" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregisterFailed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregisterRequested" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregistered" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeControllerStarted" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeControllerStopped" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeConvoyClosed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeConvoyCreated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgBound" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgGroupCreated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgInbound" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgOutbound" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgUnbound" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailArchived" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailDeleted" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailMarkedRead" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailMarkedUnread" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailRead" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailReplied" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailSent" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderCompleted" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderFailed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderFired" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeProviderSwapped" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionCrashed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionDraining" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionIdleKilled" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionQuarantined" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionStopped" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionSuspended" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionUndrained" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionUpdated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionWoke" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeWorkerOperation" + } + ], + "title": "Typed supervisor event stream envelope" + }, + "TypedTaggedEventStreamEnvelopeBeadClosed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BeadEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "bead.closed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope bead.closed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeBeadCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BeadEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "bead.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope bead.created", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeBeadUpdated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BeadEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "bead.updated", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope bead.updated", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.created", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityInitFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.init_failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.init_failed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityReady": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.ready", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.ready", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityResumed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.resumed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.resumed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCitySuspended": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.suspended", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.suspended", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityUnregisterFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregister_failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.unregister_failed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityUnregisterRequested": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregister_requested", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.unregister_requested", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityUnregistered": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregistered", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.unregistered", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeControllerStarted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "controller.started", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope controller.started", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeControllerStopped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "controller.stopped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope controller.stopped", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeConvoyClosed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "convoy.closed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope convoy.closed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeConvoyCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "convoy.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope convoy.created", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/AdapterEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.adapter_added", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.adapter_added", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/AdapterEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.adapter_removed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.adapter_removed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgBound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BoundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.bound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.bound", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgGroupCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/GroupCreatedEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.group_created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.group_created", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgInbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/InboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.inbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.inbound", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgOutbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/OutboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.outbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.outbound", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgUnbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/UnboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.unbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.unbound", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailArchived": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.archived", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.archived", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailDeleted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.deleted", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.deleted", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailMarkedRead": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.marked_read", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.marked_read", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailMarkedUnread": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.marked_unread", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.marked_unread", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailRead": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.read", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.read", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailReplied": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.replied", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.replied", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailSent": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.sent", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.sent", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeOrderCompleted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.completed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope order.completed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeOrderFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope order.failed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeOrderFired": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.fired", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope order.fired", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeProviderSwapped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "provider.swapped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope provider.swapped", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionCrashed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.crashed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.crashed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionDraining": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.draining", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.draining", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionIdleKilled": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.idle_killed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.idle_killed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionQuarantined": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.quarantined", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.quarantined", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionStopped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.stopped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.stopped", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionSuspended": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.suspended", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.suspended", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionUndrained": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.undrained", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.undrained", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionUpdated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.updated", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.updated", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionWoke": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.woke", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.woke", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeWorkerOperation": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/WorkerOperationEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "worker.operation", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope worker.operation", + "type": "object" + }, + "UnboundEventPayload": { + "additionalProperties": false, + "properties": { + "count": { + "format": "int64", + "type": "integer" + }, + "session_id": { + "type": "string" + } + }, + "required": [ + "session_id", + "count" + ], + "type": "object" + }, + "WireEvent": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/EventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor" + ], + "type": "object" + }, + "WireTaggedEvent": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/EventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "city", + "seq", + "type", + "ts", + "actor" + ], + "type": "object" + }, + "WorkerOperationEventPayload": { + "additionalProperties": false, + "properties": { + "delivered": { + "type": "boolean" }, "duration_ms": { "format": "int64", @@ -6852,7 +11184,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -6862,7 +11199,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get health" @@ -6880,7 +11222,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -6890,7 +11237,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 cities" @@ -6899,6 +11251,19 @@ "/v0/city": { "post": { "operationId": "post-v0-city", + "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + } + ], "requestBody": { "content": { "application/json": { @@ -6910,7 +11275,7 @@ "required": true }, "responses": { - "200": { + "202": { "content": { "application/json": { "schema": { @@ -6918,7 +11283,12 @@ } } }, - "description": "OK" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -6928,7 +11298,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city" @@ -6960,7 +11335,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -6970,7 +11350,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name" @@ -6978,6 +11363,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7010,7 +11406,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7020,7 +11421,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name" @@ -7030,6 +11436,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-agent-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7062,7 +11479,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7072,7 +11494,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name agent by base" @@ -7121,6 +11548,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -7132,7 +11562,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agent by base" @@ -7140,6 +11575,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-agent-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7182,7 +11628,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7192,7 +11643,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name agent by base" @@ -7254,7 +11710,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7264,7 +11725,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agent by base output" @@ -7365,7 +11831,19 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "GC-Agent-Status": { + "description": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "schema": { + "description": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "type": "string" + } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7375,7 +11853,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream agent output in real time" @@ -7385,6 +11868,17 @@ "post": { "operationId": "post-v0-city-by-city-name-agent-by-base-by-action", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7431,7 +11925,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7441,7 +11940,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name agent by base by action" @@ -7451,6 +11955,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-agent-by-dir-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7493,7 +12008,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7503,7 +12023,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name agent by dir by base" @@ -7562,6 +12087,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -7573,7 +12101,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agent by dir by base" @@ -7581,6 +12114,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-agent-by-dir-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7633,7 +12177,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7643,7 +12192,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name agent by dir by base" @@ -7715,7 +12269,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7725,7 +12284,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agent by dir by base output" @@ -7836,7 +12400,19 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "GC-Agent-Status": { + "description": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "schema": { + "description": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "type": "string" + } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7846,7 +12422,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream agent output in real time (qualified name)" @@ -7856,6 +12437,17 @@ "post": { "operationId": "post-v0-city-by-city-name-agent-by-dir-by-base-by-action", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7912,7 +12504,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7922,7 +12519,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name agent by dir by base by action" @@ -8027,6 +12629,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8038,7 +12643,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agents" @@ -8046,6 +12656,17 @@ "post": { "operationId": "create-agent", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8078,7 +12699,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8088,7 +12714,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create an agent" @@ -8098,6 +12729,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-bead-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8130,7 +12772,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8140,7 +12787,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name bead by ID" @@ -8189,6 +12841,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8200,7 +12855,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name bead by ID" @@ -8208,6 +12868,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-bead-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8250,7 +12921,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8260,7 +12936,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name bead by ID" @@ -8270,6 +12951,17 @@ "post": { "operationId": "post-v0-city-by-city-name-bead-by-id-assign", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8324,6 +13016,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8335,7 +13030,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name bead by ID assign" @@ -8345,6 +13045,17 @@ "post": { "operationId": "post-v0-city-by-city-name-bead-by-id-close", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8377,7 +13088,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8387,7 +13103,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name bead by ID close" @@ -8438,6 +13159,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8449,7 +13173,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name bead by ID deps" @@ -8459,6 +13188,17 @@ "post": { "operationId": "post-v0-city-by-city-name-bead-by-id-reopen", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8491,7 +13231,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8501,7 +13246,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name bead by ID reopen" @@ -8511,6 +13261,17 @@ "post": { "operationId": "post-v0-city-by-city-name-bead-by-id-update", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8553,7 +13314,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8563,7 +13329,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name bead by ID update" @@ -8696,6 +13467,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8707,7 +13481,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name beads" @@ -8715,6 +13494,17 @@ "post": { "operationId": "create-bead", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8765,6 +13555,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8776,7 +13569,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a bead" @@ -8827,6 +13625,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8838,7 +13639,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name beads graph by root ID" @@ -8899,6 +13705,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8910,7 +13719,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name beads ready" @@ -8951,6 +13765,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8962,7 +13779,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name config" @@ -9003,6 +13825,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9014,7 +13839,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name config explain" @@ -9046,7 +13876,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9056,7 +13891,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name config validate" @@ -9066,6 +13906,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-convoy-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9098,7 +13949,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9108,7 +13964,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name convoy by ID" @@ -9157,6 +14018,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9168,7 +14032,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name convoy by ID" @@ -9178,6 +14047,17 @@ "post": { "operationId": "post-v0-city-by-city-name-convoy-by-id-add", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9220,7 +14100,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9230,7 +14115,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name convoy by ID add" @@ -9281,6 +14171,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9292,7 +14185,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name convoy by ID check" @@ -9302,6 +14200,17 @@ "post": { "operationId": "post-v0-city-by-city-name-convoy-by-id-close", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9334,7 +14243,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9344,7 +14258,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name convoy by ID close" @@ -9354,6 +14273,17 @@ "post": { "operationId": "post-v0-city-by-city-name-convoy-by-id-remove", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9396,7 +14326,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9406,7 +14341,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name convoy by ID remove" @@ -9489,6 +14429,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9500,7 +14443,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name convoys" @@ -9508,6 +14456,17 @@ "post": { "operationId": "create-convoy", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9549,6 +14508,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9560,7 +14522,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a convoy" @@ -9673,6 +14640,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9684,7 +14654,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name events" @@ -9692,6 +14667,17 @@ "post": { "operationId": "emit-event", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9724,7 +14710,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9734,7 +14725,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Emit an event" @@ -9788,7 +14784,7 @@ { "properties": { "data": { - "$ref": "#/components/schemas/EventStreamEnvelope" + "$ref": "#/components/schemas/TypedEventStreamEnvelope" }, "event": { "const": "event", @@ -9844,7 +14840,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9854,7 +14855,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream city events in real time" @@ -9864,6 +14870,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-extmsg-adapters", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9896,7 +14913,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9906,7 +14928,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name extmsg adapters" @@ -9945,6 +14972,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9956,7 +14986,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name extmsg adapters" @@ -9964,6 +14999,17 @@ "post": { "operationId": "register-extmsg-adapter", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9996,7 +15042,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10006,7 +15057,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Register an external messaging adapter" @@ -10016,6 +15072,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-bind", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10048,7 +15115,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10058,7 +15130,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg bind" @@ -10109,6 +15186,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -10120,7 +15200,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name extmsg bindings" @@ -10202,7 +15287,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10212,7 +15302,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name extmsg groups" @@ -10220,6 +15315,17 @@ "post": { "operationId": "ensure-extmsg-group", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10252,7 +15358,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10262,7 +15373,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Ensure an external messaging group exists" @@ -10272,6 +15388,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-inbound", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10304,7 +15431,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10314,7 +15446,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg inbound" @@ -10324,6 +15461,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-outbound", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10356,7 +15504,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10366,7 +15519,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg outbound" @@ -10376,6 +15534,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-extmsg-participants", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10408,7 +15577,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10418,7 +15592,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name extmsg participants" @@ -10426,6 +15605,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-participants", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10458,7 +15648,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10468,7 +15663,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg participants" @@ -10569,6 +15769,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -10580,7 +15783,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name extmsg transcript" @@ -10590,6 +15798,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-transcript-ack", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10622,7 +15841,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10632,7 +15856,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg transcript ack" @@ -10642,6 +15871,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-unbind", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10674,7 +15914,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10684,7 +15929,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg unbind" @@ -10757,7 +16007,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10767,7 +16022,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formula by name" @@ -10819,7 +16079,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10829,7 +16094,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formulas" @@ -10893,7 +16163,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10903,7 +16178,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formulas feed" @@ -10976,7 +16256,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10986,7 +16271,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formulas by name" @@ -10996,6 +16286,17 @@ "post": { "operationId": "post-v0-city-by-city-name-formulas-by-name-preview", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11038,7 +16339,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11048,7 +16354,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name formulas by name preview" @@ -11124,7 +16435,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11134,7 +16450,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formulas by name runs" @@ -11166,7 +16487,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11176,7 +16502,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name health" @@ -11289,6 +16620,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11300,7 +16634,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name mail" @@ -11308,6 +16647,17 @@ "post": { "operationId": "send-mail", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11358,6 +16708,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11369,7 +16722,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Send a mail message" @@ -11421,7 +16779,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11431,7 +16794,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name mail count" @@ -11492,6 +16860,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11503,7 +16874,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name mail thread by ID" @@ -11513,6 +16889,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-mail-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11555,7 +16942,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11565,7 +16957,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name mail by ID" @@ -11624,6 +17021,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11635,7 +17035,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name mail by ID" @@ -11645,6 +17050,17 @@ "post": { "operationId": "post-v0-city-by-city-name-mail-by-id-archive", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11687,7 +17103,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11697,7 +17118,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name mail by ID archive" @@ -11707,6 +17133,17 @@ "post": { "operationId": "post-v0-city-by-city-name-mail-by-id-mark-unread", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11749,7 +17186,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11759,7 +17201,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name mail by ID mark unread" @@ -11769,6 +17216,17 @@ "post": { "operationId": "post-v0-city-by-city-name-mail-by-id-read", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11811,7 +17269,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11821,7 +17284,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name mail by ID read" @@ -11831,6 +17299,17 @@ "post": { "operationId": "reply-mail", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11892,6 +17371,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11903,7 +17385,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Reply to a mail message" @@ -11955,7 +17442,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11965,7 +17457,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name order history by bead ID" @@ -12007,7 +17504,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12017,7 +17519,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name order by name" @@ -12027,6 +17534,17 @@ "post": { "operationId": "post-v0-city-by-city-name-order-by-name-disable", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12059,7 +17577,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12069,7 +17592,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name order by name disable" @@ -12079,6 +17607,17 @@ "post": { "operationId": "post-v0-city-by-city-name-order-by-name-enable", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12111,7 +17650,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12121,7 +17665,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name order by name enable" @@ -12153,7 +17702,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12163,7 +17717,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name orders" @@ -12195,7 +17754,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12205,7 +17769,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name orders check" @@ -12269,7 +17838,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12279,7 +17853,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name orders feed" @@ -12345,7 +17924,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12355,7 +17939,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name orders history" @@ -12387,7 +17976,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12397,7 +17991,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name packs" @@ -12407,6 +18006,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-patches-agent-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12439,7 +18049,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12449,7 +18064,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name patches agent by base" @@ -12498,6 +18118,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12509,7 +18132,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches agent by base" @@ -12519,6 +18147,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-patches-agent-by-dir-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12561,7 +18200,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12571,7 +18215,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name patches agent by dir by base" @@ -12630,6 +18279,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12641,7 +18293,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches agent by dir by base" @@ -12682,6 +18339,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12693,7 +18353,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches agents" @@ -12701,6 +18366,17 @@ "put": { "operationId": "put-v0-city-by-city-name-patches-agents", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12733,7 +18409,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12743,7 +18424,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Put v0 city by city name patches agents" @@ -12753,6 +18439,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-patches-provider-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12785,7 +18482,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12795,7 +18497,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name patches provider by name" @@ -12844,6 +18551,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12855,7 +18565,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches provider by name" @@ -12896,6 +18611,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12907,7 +18625,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches providers" @@ -12915,6 +18638,17 @@ "put": { "operationId": "put-v0-city-by-city-name-patches-providers", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12947,7 +18681,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12957,7 +18696,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Put v0 city by city name patches providers" @@ -12967,6 +18711,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-patches-rig-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12999,7 +18754,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13009,7 +18769,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name patches rig by name" @@ -13058,6 +18823,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13069,7 +18837,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches rig by name" @@ -13110,6 +18883,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13121,7 +18897,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches rigs" @@ -13129,6 +18910,17 @@ "put": { "operationId": "put-v0-city-by-city-name-patches-rigs", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13161,7 +18953,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13171,7 +18968,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Put v0 city by city name patches rigs" @@ -13223,7 +19025,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13233,7 +19040,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name provider readiness" @@ -13243,6 +19055,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-provider-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13275,7 +19098,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13285,7 +19113,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name provider by name" @@ -13334,6 +19167,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13345,7 +19181,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name provider by name" @@ -13353,6 +19194,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-provider-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13395,7 +19247,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13405,7 +19262,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name provider by name" @@ -13446,6 +19308,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13457,7 +19322,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name providers" @@ -13465,6 +19335,17 @@ "post": { "operationId": "create-provider", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13497,7 +19378,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13507,7 +19393,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a provider" @@ -13548,6 +19439,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13559,7 +19453,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name providers public" @@ -13611,7 +19510,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13621,7 +19525,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name readiness" @@ -13631,6 +19540,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-rig-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13663,7 +19583,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13673,7 +19598,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name rig by name" @@ -13732,6 +19662,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13743,7 +19676,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name rig by name" @@ -13751,6 +19689,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-rig-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13793,7 +19742,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13803,7 +19757,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name rig by name" @@ -13813,6 +19772,17 @@ "post": { "operationId": "post-v0-city-by-city-name-rig-by-name-by-action", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13855,7 +19825,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13865,7 +19840,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name rig by name by action" @@ -13936,6 +19916,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13947,7 +19930,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name rigs" @@ -13955,6 +19943,17 @@ "post": { "operationId": "create-rig", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13987,7 +19986,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13997,7 +20001,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a rig" @@ -14048,6 +20057,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14059,7 +20071,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name service by name" @@ -14069,6 +20086,17 @@ "post": { "operationId": "post-v0-city-by-city-name-service-by-name-restart", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14101,7 +20129,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14111,7 +20144,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name service by name restart" @@ -14152,6 +20190,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14163,7 +20204,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name services" @@ -14224,6 +20270,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14235,7 +20284,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID" @@ -14243,6 +20297,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-session-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14294,6 +20359,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14305,7 +20373,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name session by ID" @@ -14356,6 +20429,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14367,7 +20443,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID agents" @@ -14428,6 +20509,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14439,7 +20523,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID agents by agent ID" @@ -14449,6 +20538,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-close", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14491,7 +20591,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14501,7 +20606,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID close" @@ -14511,6 +20621,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-kill", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14543,7 +20664,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14553,7 +20679,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID kill" @@ -14563,6 +20694,17 @@ "post": { "operationId": "send-session-message", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14605,7 +20747,12 @@ } } }, - "description": "Accepted" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14615,7 +20762,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Send a message to a session" @@ -14666,6 +20818,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14677,7 +20832,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID pending" @@ -14687,6 +20847,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-rename", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14738,6 +20909,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14749,7 +20923,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID rename" @@ -14759,6 +20938,17 @@ "post": { "operationId": "respond-session", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14801,7 +20991,12 @@ } } }, - "description": "Accepted" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14811,7 +21006,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Respond to a pending interaction" @@ -14821,6 +21021,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-stop", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14853,7 +21064,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14863,7 +21079,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID stop" @@ -15051,7 +21272,26 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "GC-Session-State": { + "description": "Session state at the time streaming began (e.g. active, closed).", + "schema": { + "description": "Session state at the time streaming began (e.g. active, closed).", + "type": "string" + } + }, + "GC-Session-Status": { + "description": "Runtime status at the time streaming began. Emitted as \"stopped\" when the session's underlying process is not running.", + "schema": { + "description": "Runtime status at the time streaming began. Emitted as \"stopped\" when the session's underlying process is not running.", + "type": "string" + } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15061,7 +21301,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream session output in real time" @@ -15071,6 +21316,17 @@ "post": { "operationId": "submit-session", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15113,7 +21369,12 @@ } } }, - "description": "Accepted" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15123,7 +21384,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Submit a message to a session" @@ -15133,6 +21399,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-suspend", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15165,7 +21442,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15175,7 +21457,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID suspend" @@ -15256,6 +21543,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -15267,7 +21557,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID transcript" @@ -15277,6 +21572,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-wake", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15309,7 +21615,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15319,7 +21630,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID wake" @@ -15412,6 +21728,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -15423,7 +21742,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name sessions" @@ -15431,6 +21755,17 @@ "post": { "operationId": "create-session", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15463,7 +21798,12 @@ } } }, - "description": "Accepted" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15473,7 +21813,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a session" @@ -15483,6 +21828,17 @@ "post": { "operationId": "post-v0-city-by-city-name-sling", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15515,7 +21871,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15525,7 +21886,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name sling" @@ -15586,6 +21952,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -15597,16 +21966,93 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name status" } }, + "/v0/city/{cityName}/unregister": { + "post": { + "operationId": "post-v0-city-by-city-name-unregister", + "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, + { + "description": "Supervisor-registered city name.", + "in": "path", + "name": "cityName", + "required": true, + "schema": { + "description": "Supervisor-registered city name.", + "type": "string" + } + } + ], + "responses": { + "202": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CityUnregisterResponse" + } + } + }, + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } + } + }, + "summary": "Post v0 city by city name unregister" + } + }, "/v0/city/{cityName}/workflow/{workflow_id}": { "delete": { "operationId": "delete-v0-city-by-city-name-workflow-by-workflow-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15669,7 +22115,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15679,7 +22130,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name workflow by workflow ID" @@ -15748,6 +22204,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -15759,7 +22218,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name workflow by workflow ID" @@ -15821,7 +22285,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15831,7 +22300,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 events" @@ -15898,7 +22372,7 @@ { "properties": { "data": { - "$ref": "#/components/schemas/TaggedEventStreamEnvelope" + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelope" }, "event": { "const": "tagged_event", @@ -15928,7 +22402,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15938,7 +22417,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream tagged events from all running cities." @@ -15978,7 +22462,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15988,7 +22477,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 provider readiness" @@ -16028,7 +22522,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -16038,7 +22537,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 readiness" diff --git a/docs/troubleshooting/dolt-bloat-recovery.md b/docs/troubleshooting/dolt-bloat-recovery.md index 12eda71b99..7589902215 100644 --- a/docs/troubleshooting/dolt-bloat-recovery.md +++ b/docs/troubleshooting/dolt-bloat-recovery.md @@ -85,13 +85,20 @@ If GC finishes but the size barely moves, the chunks are nearly all live floor; newer releases ship improved auto-GC heuristics and default archive compression. - **Let the dolt pack's `dolt-gc-nudge` order run continuously.** It - ships embedded in the dolt pack and fires every 6h by default. The - nudge catches the "bloated-but-stable" corner case where no single - write burst crosses Dolt's 125 MB auto-GC threshold. To opt out on a - given city, add `dolt-gc-nudge` to the city's `[orders] skip = [...]` - list (or to a rig-level `[[order.override]]`). Tune the trigger size - via the `GC_DOLT_GC_THRESHOLD_BYTES` environment variable - (default: 2 GiB) in the city's environment. + ships embedded in the dolt pack and fires `CALL DOLT_GC()` every 1h + by default, unconditionally. Gas City's managed-Dolt launch path now + forces `DOLT_GC_SCHEDULER=NONE`, which restores Dolt's configured + auto-GC behavior on multi-core hosts affected by + [dolthub/dolt#10944](https://github.com/dolthub/dolt/issues/10944). + The hourly nudge remains valuable as a belt-and-suspenders backstop + for the bd workload and as an unconditional recovery path if the + threshold-triggered auto-GC has nothing to do for a while. GC is + idempotent and near-free when there's nothing to reclaim, so running + it every hour is cheap. To opt out on a given city, add + `dolt-gc-nudge` to the city's `[orders] skip = [...]` list (or to a + rig-level `[[order.override]]`). To skip GC on small databases, set + `GC_DOLT_GC_THRESHOLD_BYTES` to a positive byte count in the city's + environment (default: 0 — run unconditionally). - **Mind `orders.max_timeout` if you set one.** The nudge order asks for a 24-hour timeout to accommodate serialized `CALL DOLT_GC()` runs on large stores. A city-level `orders.max_timeout` below 24h will cap the diff --git a/engdocs/architecture/api-control-plane.md b/engdocs/architecture/api-control-plane.md new file mode 100644 index 0000000000..e0dd0b69da --- /dev/null +++ b/engdocs/architecture/api-control-plane.md @@ -0,0 +1,663 @@ +--- +title: "API Control Plane" +description: "Current-state architecture for Gas City's CLI, HTTP, SSE, generated client, and typed-wire contract." +--- + +> Last verified against code: 2026-04-22 + +This architecture doc captures the API control-plane invariants Gas +City has converged on. It is normative current-state documentation: +future contributions that violate these invariants are wrong unless a +conscious decision updates this document. Plans in `plans/archive/` +describe the journeys that produced these invariants; this document +describes the destination. + +Two architectural themes run through everything below: + +1. **The object model is the center; the CLI and the HTTP + SSE API + are projections over it.** One canonical domain, two typed + surfaces. +2. **Typed data end-to-end.** Go structs with annotations drive a + generated OpenAPI 3.1 contract; every wire-visible shape appears + in the OpenAPI spec; consumers in any language code against the same + contract. Zero opacity on the wire. + +## 1. The object model + +`internal/{beads, mail, convoy, formula, agent, events, session, +sling, graphroute, agentutil, pathutil, cityinit, ...}` is the +canonical domain. All business logic lives there. The two surfaces +below call into it; neither re-implements validation, routing, or +invariants. + +City initialization is a worked example: the HTTP handler for +`POST /v0/city` does **not** shell out to `gc init`; it calls +`cityinit.Initializer.Scaffold` in-process, which is the same +entry point the CLI drives. The scaffolded city registers with +the supervisor synchronously before `202 Accepted` returns; the +reconciler runs the slow finalize later and publishes `city.ready` +/ `city.init_failed` events. Both projections live on the same +typed contract and error sentinels +(`cityinit.ErrAlreadyInitialized`, `ErrInvalidProvider`, +`ErrMissingDependency`, `ErrProviderNotReady`, +`ErrInvalidBootstrapProfile`). Long-running mutations in general +follow this shape: scaffold synchronously, return 202, publish +completion events — subscribers watch the event stream instead of +polling. + +``` +cmd/gc/cmd_*.go internal/api/handler_*.go + (arg parsing, (Huma input/output types, + text formatting, handler bodies, + exit codes) typed error returns) + \ / + \ / + v v + internal/sling/ internal/convoy/ + internal/agentutil/ internal/graphroute/ + internal/pathutil/ + | + v + internal/{beads, config, formula, molecule, agent, events, ...} +``` + +### Invariants + +- **Domain code has no I/O surfacing.** No `fmt.Fprintf`, no + `io.Writer` parameters, no HTTP responses. Domain functions + return values and errors. Text formatting is a CLI concern; JSON + shaping is an API concern. +- **Narrow interfaces over flag bags.** Domain-side dependencies + use focused interfaces (`AgentResolver`, `BeadRouter`, + `Notifier`, `BranchResolver`) validated at construction. +- **Intent-based APIs.** Callers express intent (`RouteBead`, + `LaunchFormula`, `AttachFormula`, `ExpandConvoy`); implementation + decides how (shell command, direct store, API call). No + god-struct option bags passed around. +- **No upward dependencies.** A lower layer never imports from a + higher layer. + +## 2. Projections: CLI and HTTP + SSE API + +### CLI projection (`cmd/gc/`) + +The CLI calls the core library directly. It is not a generic +remote client; it coexists with a local supervisor in the same city +by routing through HTTP only when lock coordination requires it. + +Concretely, `cmd/gc/apiroute.go:apiClient()` implements this rule: + +- **No running local supervisor** → CLI calls the core library + directly against the on-disk stores. +- **Running local supervisor with mutations allowed** → CLI routes + the mutation through the local HTTP API via the generated Go + client. The supervisor executes the mutation under its own + locks; the CLI's result is consistent with the supervisor's + state. + +Remote access is not the first-class reason this path exists. A +`--base-url http://remote:port` invocation is a side effect of the +same mechanism, not its purpose. The generated client is "library +calls dispatched over HTTP when we have to cross a process +boundary we didn't create." + +### API projection (`internal/api/`) + +Every HTTP + SSE endpoint is registered through Huma against +annotated Go types. Huma generates the OpenAPI 3.1 spec from those +types; the spec drives everything downstream. + +### The generated Go client + +`internal/api/genclient/` has three in-tree consumer categories, +governed by a structural rule: **direct consumption is allowed for +endpoints that (a) do not participate in write-side fallback (no +`ShouldFallback` path) and (b) do not require domain-type conversion +at the adapter seam.** Anything that fails either test goes through +`internal/api/client.go`. + +1. **CLI mutation coordination** via `internal/api/client.go`, used + by `cmd/gc/apiroute.go` as described above. This is the only + consumer for paths that mutate state and could race an in-process + supervisor, or that need domain-type conversion (e.g. typed + `session.SubmitIntent` from a string wire field). The adapter + also owns local-file fallback when the controller isn't running. +2. **Read/stream CLI surfaces that import genclient directly** — + currently `cmd/gc/cmd_events.go`, which calls typed methods for + event listing and SSE following. Events have no write-side + fallback (no bus without a controller) and need no domain-type + conversion, so they satisfy the structural rule. Future + read-only CLI surfaces that meet the same two conditions are + allowed to import genclient directly; no case-by-case approval + needed. +3. **Layer 2 conformance probe** — + `genclient_roundtrip_test.go` exercises every generated method + against a real supervisor so spec/reality drift fails CI. + +The generated client is not promoted as a public Go SDK for +external consumers. External Go consumers, if they ever appear, +get a supported surface at that point; until then the `internal/` +location is load-bearing. + +### The dashboard projection + +The dashboard is a static TypeScript SPA served by a tiny Go +binary (`cmd/gc/dashboard/`) whose only jobs are to embed the +compiled bundle and inject the supervisor URL into `index.html`. +The SPA talks directly to the supervisor's typed OpenAPI endpoints +from the browser — the dashboard server is NOT an API proxy. The +dashboard server also hosts one narrow operational debug endpoint +(`/__client-log`) that accepts browser error logs for centralized +debugging; this endpoint is intentionally outside the typed HTTP + +SSE control plane and may use standard `encoding/json` for body +decoding. + +## 3. The typed-wire principle + +The invariants below apply to every operation under `internal/api/` +except the `/svc/*` workspace-service proxy (see §5). + +### 3.1 Annotations drive the live implementation + +Each endpoint is a Go function whose signature (typed input struct, +typed output struct) plus a `huma.Operation` value IS the endpoint +definition. Huma binds it, validates it, routes it, serializes it, +schema-describes it. There is no second description of the endpoint +anywhere — not in a router table, not in an OpenAPI YAML, not in a +client stub. + +Framework-level cross-cutting wire contract (CSRF header, request-ID +response header, per-stream status headers) does not live on +per-endpoint struct annotations; it lives on the registration +helpers (`cityPost`, `cityRegister`, `registerSSE`) or on a +post-registration spec walker (`registerFrameworkHeaders`). That is +not a second description — it is the same mechanism applied +at one layer up, and the OpenAPI spec that results still describes +every operation's full contract. See §3.5.2. Patterns and Huma +quirks that inform these helpers are documented in +[Huma Usage Notes](../contributors/huma-usage.md). + +### 3.2 Spec is generated, never hand-written + +`internal/api/openapi.json` and `docs/schema/openapi.json` are +outputs of `cmd/genspec`, which reads the live Huma registration +from a `SupervisorMux`. The pre-commit hook regenerates both on +every Go-file commit. `TestOpenAPISpecInSync` fails CI if the +committed spec drifts from what the supervisor serves. + +### 3.3 The routes we register ARE the routes we expose + +Per-city operations live at `/v0/city/{cityName}/...`. +Supervisor-scope operations live at their top-level paths. No +shadow mapping. No `prefix-strip-and-forward`. No client-side +path-rewrite helpers. The existence of such a helper is direct +evidence the spec disagrees with reality and is a bug to fix. + +### 3.4 No hand-constructed JSON for domain data + +Every wire byte that represents a domain value comes from encoding +a typed Go struct (schema-registered with Huma) through the +standard JSON encoder, directly or via Huma's own serialization +machinery. This principle forbids three anti-patterns specifically: + +- `json.Marshal(map[string]any{...})` — untyped input. +- `fmt.Sprintf`-built JSON strings — hand-constructed shape. +- `json.Marshal(anyInterfaceValue)` where the interface carries + values whose types are not schema-registered — hides the shape + from the spec. + +The test a reviewer applies: *is there any line in your code that +produces JSON-shaped output from non-typed or map-typed input?* If +yes, violation. If every JSON byte comes from `encoder.Encode` of a +typed, schema-registered struct, the principle holds. + +Protocol framing around domain data — HTTP status codes, HTTP +response headers, SSE `id:` / `event:` / `data:` / retry line +separators, chunked-encoding bytes — is not domain data and is not +in scope for this principle. The carve-out is direction-symmetric +and covers two specific files: `internal/api/sse.go` (emitter) +hand-writes the SSE protocol-text lines around a typed +`encoder.Encode(data)` call on a registered struct, and +`cmd/gc/cmd_events.go:sseDecoder` (consumer) hand-parses the same +SSE protocol-text lines and `json.Unmarshal`s the `data:` payload +into typed `genclient.*` structs. In both directions the domain +payload IS framework-encoded/decoded; the surrounding protocol +literals are not JSON at all. + +New SSE endpoints must register through `registerSSE` / +`registerSSEStringID`; ad-hoc SSE handlers outside those helpers +are not covered by this carve-out. + +Edge cases that are NOT wire and therefore exempt: + +- SQL/BLOB (de)serialization in storage packages. +- Hashing request bodies for idempotency keys. +- Parsing stored JSONL transcript/log files from disk. +- Parsing external-tool output we don't own (provider CLI stdout, + provider auth files like `~/.codex/auth.json`). +- Internal event-bus `[]byte` payloads between in-process emitters + and consumers (these become typed at the wire via the registry — + see §4). + +Custom `MarshalJSON` / `UnmarshalJSON` on wire types are forbidden +with two narrow, documented exceptions: + +- **`SessionRawMessageFrame`** (`internal/api/session_frame_types.go`) + — the raw-frame pass-through for provider-native session + transcripts; forwards arbitrary JSON the provider wrote. See §3.6. +- **`EventPayloadUnion`** (`internal/api/convoy_event_stream.go`) + — the wire wrapper around `events.Payload` that emits the typed + payload as a named `oneOf` component. Its `MarshalJSON` emits + the concrete variant directly (so the wire sees `{"rig":...}` + rather than a wrapper object); its Schema method registers and + refs the named component. Required to get a single named + `EventPayload` component schema that Go and TS clients can both + consume. + +### 3.5 Typed structs for every shape knowable at compile time + +Every response field, every SSE event payload, every input body is +a named Go struct with real fields and Huma tags. No +`json.RawMessage` or `map[string]any` in the typed control plane, +with exactly one class of exception (§3.6). + +"Heterogeneous", "opaque", "clients render it generically", "we'll +figure out the union later", and "it's just internal" are not +qualifying exceptions. If our code constructs the map, we know the +keys. Make it a struct. + +### 3.5.1 No hidden inputs — every accepted parameter appears in the spec + +Every input a handler reads MUST be a typed field on its Huma +input struct (`path:`, `query:`, `header:`, or `Body`). The +generated OpenAPI spec is the complete and exhaustive description +of the inputs an endpoint accepts. Running a request through a +handler must not produce a different outcome than running the same +request through the spec. + +Three anti-patterns are specifically forbidden: + +- **Dynamic or wildcard query parameters.** Any scheme where a + handler accepts query keys matching a pattern (`var.*`, `meta_*`, + `x-*`) rather than declared names. OpenAPI 3.1 cannot express + wildcard query keys; accepting them creates a hidden contract + the spec cannot describe. When a handler needs an open-ended + string-to-string dictionary as input, move the input into a + typed request body field (`Vars map[string]string` on a POST + body). Dictionary bodies have a schema; dictionary query + parameters do not. +- **Resolvers that read raw URL query or header values that + aren't declared input fields.** `huma.Resolver` implementations + may validate or normalize values the struct already declares, + but may not read keys off `ctx.URL().Query()` or `ctx.Header()` + that aren't present on the input struct. If a resolver needs a + value, that value is a declared field — no exceptions. +- **Presence-vs-empty semantics via raw-URL inspection.** If a + handler behaves differently for "parameter absent" vs "parameter + present with empty value", the presence flag must come from + Huma's parameter binder — not from peeking at `ctx.URL().Query()` + inside a Resolver. Use the `huma.OptionalParam[T]`-style wrapper + (see `internal/api/huma_optional_param.go`): a custom type with + `Schema()`, `Receiver()`, and `OnParamSet(isSet bool, ...)` that + emits the underlying `T`'s schema on the wire and exposes an + `IsSet` flag to the handler. Huma v2 does not support pointer + query parameters (they panic at registration, see + `github.com/danielgtaylor/huma` issue #288); `OptionalParam` is + the framework-sanctioned idiom. + + Practical corollary: Huma's parameter binder treats `?cursor=` + (empty value) identically to an absent parameter + (`huma.go:881-882: isSet = value != ""`). A three-state contract + (absent / present-empty / present-nonempty) is therefore not + expressible against Huma; the wire contract collapses to + two states. Design APIs around that. + +The test a reviewer applies: does running an undeclared query +parameter or an undeclared body field through the handler change +its behavior? If yes, violation. The spec is the contract; the +handler does not get a second, private contract the spec doesn't +know about. + +Huma does not reject undeclared query parameters by default +(they are silently ignored). That is not permission to rely on +them — silent acceptance of undeclared parameters is a property +of the framework, not a blessing of hidden contract. Callers that +send undeclared parameters are sending noise; handlers that read +them are violating this principle. + +### 3.5.2 Framework-level headers declared once, not per-operation + +Wire contract that applies uniformly across every operation — +CSRF request headers, request-ID response headers, the custom +response headers SSE streams emit for runtime status — is still +real wire contract and must appear in the spec. OpenAPI 3.1 has +no mechanism to declare headers "globally" for all operations +(see +[speakeasy.com/openapi/responses/headers](https://www.speakeasy.com/openapi/responses/headers)); +the canonical pattern is: + +1. Define the header once. Request headers live as operation + parameters; response headers get a named entry in + `components.headers`. +2. Reference it from every operation it applies to (request + params go on the Operation's `Parameters`; response headers + use `{"$ref": "#/components/headers/NAME"}`). + +Rather than embedding the reference in 50+ input/output structs, +attach it at the single function every operation already flows +through: + +- **Request headers (e.g. `X-GC-Request`)** — `cityPost`, + `cityPut`, `cityPatch`, `cityDelete`, and `cityRegister` in + `internal/api/city_scope.go` pass `addMutationCSRFParam` as a + Huma operation handler. One line at the route helper covers + every current and future mutation endpoint. +- **Response headers (e.g. `X-GC-Request-Id`)** — + `registerFrameworkHeaders` in + `internal/api/huma_spec_framework.go` runs once after all + routes are registered. It populates `components.headers` and + walks every operation's responses to inject a `$ref` pointing + at the named component. +- **Per-stream custom response headers (e.g. `GC-Agent-Status`, + `GC-Session-State`, `GC-Session-Status`)** — catalogued in + `sseStatusHeaders` (`internal/api/sse.go`) and referenced by + name at each `registerSSE` call site via + `sseResponseHeaders("GC-Agent-Status")`. Colocated with the + operation where the handler emits the header, one catalog + entry per header. + +These patterns are not exceptions to §3.5.1; they are the +§3.5.1-compliant mechanism for cross-cutting concerns. The spec +still fully describes the contract — every operation's parameters +and response headers list the header explicitly — but the +declaration happens at one function call, not fifty struct +definitions. Middleware remains the single source of enforcement; +the spec remains the single source of description; the helpers +keep the two aligned. + +### 3.6 Raw pass-through for provider-native session frames + +Session transcript streaming and query endpoints forward +provider-native frames with full fidelity. Each response/envelope +identifies the producing provider via a `provider` field whose +value is one of the known provider keys (`claude`, `codex`, +`gemini`, `open-code`, etc.); each frame's JSON is emitted verbatim +as the provider wrote it, with no GC-side interpretation. +Consumers parse frames using provider-specific logic on their side, +keyed by the provider identifier on the envelope. + +The single JSON-pass-through wire type is `SessionRawMessageFrame` +(`internal/api/session_frame_types.go`). Its Schema method emits +an "any JSON value" schema because Gas City does not own the +shape of provider frames. Publishing typed wire schemas for +provider frames would claim a contract we don't own: a provider +could change its frame shape tomorrow and the spec would silently +lie until regenerated. Honest opacity with a provider discriminator +is the right design. + +Passing through externally-authored shapes is not a license to +also opacify our own shapes that happen to be nested near them. +Every GC-owned field on the same envelope as the raw frames +(envelope metadata, provider identifier, session info) stays +typed. + +### 3.7 Every event type has a typed wire payload + +See §4. + +### 3.8 Error responses are typed too + +Every error returned by a Huma handler is a +`huma.StatusError`-producing call with a real problem-details +body. No `apiError{}` shortcuts. No hand-written `writeError`. + +For the outermost panic-recovery middleware (which must run before +Huma enters the stack), error bodies are pre-serialized +`application/problem+json` byte constants — one `var` declaration +per well-known error, no runtime `json.Marshal`. The constants +live in `internal/api/middleware.go` as `problemBody` values. + +### 3.9 `/svc/*` is the only exclusion + +`/svc/*` is a raw pass-through to external service processes that +own their own contracts. It is explicitly not a typed API +surface. This is the single carved-out path inside `internal/api/`. +If `/svc/*` ever becomes typed, it gets its own migration. + +## 4. Event typing (the registry) + +Events are a first-class part of the typed wire contract. Both the +SSE streams (`/v0/events/stream`, +`/v0/city/{cityName}/events/stream`) and the list endpoints +(`GET /v0/events`, `GET /v0/city/{cityName}/events`) describe their +`payload` field as a named `oneOf` union covering every registered +`events.Payload` shape. There is no opaque `payload: {}` anywhere +on the wire. + +### Mechanism + +- **Bus layer (`internal/events`)** stores payloads as `[]byte` so + it stays domain-agnostic. `events.Event` and `events.TaggedEvent` + are bus-internal types only; they are never returned directly + from an HTTP handler. +- **Registry (`internal/events/payload.go`)** holds the event-type + → Go-type mapping. `events.RegisterPayload(typeConst, sample)` + associates a constant with a sample value of a type implementing + the sealed `events.Payload` interface. `events.DecodePayload` + turns bus bytes back into the registered typed value. +- **Emitters** take values of `events.Payload` rather than + `map[string]any`. The sealed interface keeps ad-hoc shapes out + of emission sites at compile time. +- **Wire projection** — the API-layer `WireEvent` / + `WireTaggedEvent` types (list) and `eventStreamEnvelope` / + `taggedEventStreamEnvelope` (SSE) carry a typed `Payload` field + wrapped in `EventPayloadUnion`. `EventPayloadUnion.Schema` + registers a named `EventPayload` component whose schema is a + `oneOf` of every registered payload type. + +### Registry coverage + +Every constant in `events.KnownEventTypes` MUST have a registered +payload. Events that carry no structured data register +`events.NoPayload` — a typed empty struct that still produces a +named schema variant so the wire stays uniform across event types. + +`TestEveryKnownEventTypeHasRegisteredPayload` fails CI if a new +constant is added without registration; that's how the registry +discipline stays load-bearing rather than best-effort. + +**Decode-failure policy (uniform across list and stream).** Decode +failures and unregistered event types are omitted from list and +stream output and logged via `log.Printf`; the wire never carries +a degraded envelope with nil payload. A malformed event is a CI +bug (the registry-coverage test above catches it before prod); +emitting a typed envelope with `payload: null` would train +consumers to tolerate broken payloads, defeating the point of +§3.4. Clean omission plus a loud log is the contract. + +### Discrimination design + +The envelope carries a plain `type: string` field; the `payload` +field is the discriminated `oneOf` union. Consumers switch on +`type` and narrow `payload` explicitly: + +```typescript +if (event.type === "mail.sent") { + use(event.payload as MailEventPayload); +} +``` + +Envelope-level discrimination — each event-type constant pinned +as a `type` const in its own envelope variant, with OpenAPI 3.1 +discriminators giving consumers automatic narrowing — would be +nicer. It is not the design because no current Go OpenAPI client +generator produces a workable Go type from envelope-level +`oneOf`: + +- **oapi-codegen** collapses the envelope to a `json.RawMessage` + wrapper that loses all field access — `cmd/gc/cmd_events.go`'s + field-based construction breaks. +- **ogen** drops `text/event-stream` operations entirely — + the events streams disappear from the generated client. + +The payload-field-union design is the current ceiling. Every +payload variant is still fully typed on the wire; consumers narrow +explicitly rather than getting automatic discriminator narrowing. +See §6 for the full tooling note. + +## 5. Developer workflow + +The invariants above exist so the developer's contribution to the +HTTP + SSE surface is Go code only. Tooling produces everything +else. + +### Adding or changing a REST operation + +1. Edit or add input/output struct types with Huma tags + (`json:"..."`, `minLength:"1"`, `required:"true"`, etc.). +2. Write the handler function; register via `huma.Register` (or + the `cityGet` / `cityPost` / `cityPatch` / etc. helpers in + `internal/api/city_scope.go` for per-city scoped operations). +3. Commit. Pre-commit regenerates `internal/api/openapi.json`, + `docs/schema/openapi.json`, `internal/api/genclient/`, and the + TS types under `cmd/gc/dashboard/web/src/generated/`. Mintlify + publishes the spec on the next docs build. + +### Adding or changing an event type + +1. Add the constant to `internal/events/events.go` and append it + to `events.KnownEventTypes`. +2. Define a typed payload struct implementing `events.Payload` (a + trivial `IsEventPayload()` method), or use `events.NoPayload` + for events whose envelope fields alone capture the semantics. +3. Call `events.RegisterPayload(constant, sample)` from an + `init()` in the domain package that owns the event (e.g. + `internal/api/event_payloads.go` for mail/bead; + `internal/extmsg/events.go` for extmsg). +4. Commit. Pre-commit regenerates the discriminated-union wire + schema; generated clients gain the new typed variant + automatically. + +### CI guards + +Skipping any step lands on a CI failure, not a production bug: + +| Miss | Caught by | +|---|---| +| Spec not regenerated after Go-type change | `TestOpenAPISpecInSync` | +| Generated Go client out of sync with spec | `TestGeneratedClientInSync` | +| Handler response field undeclared in spec | Layer 1 response-validation tests | +| Spec/client method-shape drift | Layer 2 round-trip tests (`genclient_roundtrip_test.go`) | +| End-to-end binary wire regression | Layer 3 integration tests (`//go:build integration`) | +| New event-type constant without registered payload | `TestEveryKnownEventTypeHasRegisteredPayload` | +| Hard-coded SPA `/v0/...` path outside typed client | TypeScript build (`satisfies SpecPath` in `api.ts`) | + +## 6. Tooling landscape + +Principle 7's "payload-field-level discrimination rather than +envelope-level" is a Go-tooling constraint, not a principled +preference. The TypeScript and Go ecosystems differ on what they +support; this section records what we evaluated and what we use +per language. + +### Go (server-side Huma, client via oapi-codegen) + +- **Huma v2** — server framework. Generates OpenAPI 3.1 from + annotated Go types; we use it for every typed endpoint. Emits a + 3.0 downgrade on request for consumers that still need 3.0. +- **oapi-codegen** — our current Go client generator. Supports + OpenAPI 3.0 (we feed it the downgrade from Huma). When given + envelope-level `oneOf`, it generates `struct { union + json.RawMessage }` with `AsX`/`FromX`/`MergeX` accessor methods. + That shape breaks field-based construction in + `cmd/gc/cmd_events.go`. It does generate typed request methods + for SSE endpoints, but does not parse SSE frames — the caller + handles framing. +- **ogen** — evaluated via spike. Refuses `text/event-stream` + content type entirely; every SSE endpoint is dropped from the + generated client. With `ignore_not_implemented: all`, ogen + produces clean REST types but drops SSE operations Gas City is + built on. Not viable. +- **openapi-generator** (Java-based) — breaks the pure-Go toolchain + and generates less-idiomatic Go. +- **Commercial SDK generators** (Speakeasy, Fern, Stainless) — + generate typed Go SSE clients including envelope-level `oneOf` + handling. Not open source; paid plans start at ~$250/mo. + +The payload-field-union `EventPayload` design (Principle 7) is the +current ceiling under open-source Go tooling. Revisit if +oapi-codegen's experimental 3.1/3.2-aware branch stabilizes or if +another open-source Go generator ships envelope-level `oneOf` plus +SSE that works with our shape. + +### TypeScript (dashboard SPA) + +- **`openapi-fetch`** — typed `fetch` wrapper, the tool the + dashboard uses for every REST call site. Typed path/body/response + against `openapi-typescript`-generated `schema.d.ts`. Minimal + runtime, well-documented, keeps REST call-site code short. Does + not handle SSE — that's what drives the dual-tool design below. +- **`@hey-api/openapi-ts`** — open-source generator the dashboard + uses exclusively for SSE. Generates typed stream functions using + `fetch()` + `ReadableStream` (not `EventSource`), which means + custom auth headers work, retry with exponential backoff is + built in, and each stream has typed discriminated-union response + types keyed by the SSE `event` name. `sse.ts` is a thin callback + bridge over the generated `streamSupervisorEvents`, + `streamEvents`, and `streamSession` functions; the per-frame + JSON parsing, line buffering, and retry are all framework code. +- **`openapi-typescript-codegen`** — unmaintained. +- **OpenAPI Generator** (Java) — same pure-toolchain concern as Go. + +The dual-tool design is pragmatic, not aspirational: each library +handles what it's good at. `openapi-fetch` is the minimal typed +surface for REST consumers (kept because it has zero impact on +call-site code and the ecosystem has shifted to hey-api slowly +enough that we'd gain nothing by churning every REST call today). +`@hey-api/openapi-ts` is the only open-source TS tool that +generates typed SSE stream clients, and it handles every aspect of +the SSE wire that used to be hand-rolled in `sse.ts`. + +The Go-side `oneOf` ceiling described above does not apply to +TypeScript consumers. SSE frames come typed and discriminated +through the generated stream functions; consumers get automatic +`switch (frame.event)` narrowing with no hand-written parser or +type guard in the SPA. + +## 7. What is out of scope + +- **`/svc/*` proxy.** See §3.9. +- **Outbound HTTP** (`internal/extmsg/http_adapter.go`, + `internal/workspacesvc/proxy_process.go`). Not typed API + endpoints; we consume someone else's contract. +- **Storage-layer (de)serialization** (SQL BLOBs, JSONL log files, + external-tool auth files). Not on our wire. +- **Generated Go client as a Go SDK surface.** Stays in + `internal/` until external consumers show up. +- **WebSocket transport.** HTTP + SSE only. OpenAPI 3.1 + Huma + covers SSE end-to-end, so AsyncAPI / Modelina are not in play. + +## 8. Maintenance rule + +Every file-path citation in this document is load-bearing. If you +rename or remove a cited symbol (`events.KnownEventTypes`, +`EventPayloadUnion`, `TestEveryKnownEventTypeHasRegisteredPayload`, +`cmd/gc/apiroute.go:apiClient()`, `addMutationCSRFParam`, +`registerFrameworkHeaders`, `sseResponseHeaders`, +`OptionalParam`, `cityinit.Initializer`, `cityinit.InitRequest`, +`cityinit.InitResult`, `cityinit.UnregisterRequest`, +`cityinit.UnregisterResult`, `cityinit.ErrNotRegistered`, +`TransientCityEventSource`, etc.), **update this document in the same +commit**. Stale architecture docs are worse than no docs — they +mislead future agents about what invariants hold. + +Framework-specific patterns and Huma quirks are captured in +[Huma Usage Notes](../contributors/huma-usage.md); update that file +in the same commit when you touch any of: `OptionalParam`, +`addMutationCSRFParam`, `registerFrameworkHeaders`, +`sseResponseHeaders`, the SSE hand-writing zone, or the +`cityPost`/`cityRegister` helper family. + +Line numbers are deliberately omitted so the spec survives +refactors. Package names, type names, and test names are stable +anchors. diff --git a/engdocs/architecture/beads.md b/engdocs/architecture/beads.md index 17c701c058..8ef533a6c8 100644 --- a/engdocs/architecture/beads.md +++ b/engdocs/architecture/beads.md @@ -180,10 +180,10 @@ enforced by the conformance suite in `internal/beads/beadstest/conformance.go`. `internal/beads/` (and `test/integration/`) directly invokes the bd binary. -14. **BdStore maps bd's extended statuses to Gas City's three.** bd uses - open, in_progress, blocked, review, testing, closed. Gas City maps - closed to closed, in_progress to in_progress, and everything else - to open. +14. **BdStore maps backend statuses onto Gas City's three-state contract.** + bd uses open, in_progress, blocked, review, testing, closed. Gas City + exposes open, in_progress, closed, so BdStore maps blocked/review/testing + to open and normalizes an empty backend status to open. 15. **FileStore uses atomic writes.** Persistence writes go to a temp file first, then `os.Rename` to the target path -- never partial diff --git a/engdocs/architecture/index.md b/engdocs/architecture/index.md index d3561bdb0e..25fd3a19a8 100644 --- a/engdocs/architecture/index.md +++ b/engdocs/architecture/index.md @@ -48,9 +48,12 @@ Each is provably composable from the primitives. ### Infrastructure -12. **[Controller](./controller.md)** — the main loop: config watch, +12. **[API Control Plane](./api-control-plane.md)** — CLI/API projections, + typed HTTP + SSE wire contract, generated clients, and event payload + registry +13. **[Controller](./controller.md)** — the main loop: config watch, reconciliation tick, order dispatch -13. **[Orders](./orders.md)** — trigger-conditioned formula/exec +14. **[Orders](./orders.md)** — trigger-conditioned formula/exec dispatch, rig-scoped labels ### End-to-End Traces @@ -58,9 +61,9 @@ Each is provably composable from the primitives. These trace a concrete operation through all layers. The most effective way to understand how the system fits together. -14. **[Life of a Bead](./life-of-a-bead.md)** — create → hook → claim → +15. **[Life of a Bead](./life-of-a-bead.md)** — create → hook → claim → execute → close -15. **[Life of a Molecule](./life-of-a-molecule.md)** — formula parse → +16. **[Life of a Molecule](./life-of-a-molecule.md)** — formula parse → dispatch → molecule create → step execution → completion ## Document Types diff --git a/engdocs/architecture/life-of-a-bead.md b/engdocs/architecture/life-of-a-bead.md index 91bf8da6c3..d6cea8c4ec 100644 --- a/engdocs/architecture/life-of-a-bead.md +++ b/engdocs/architecture/life-of-a-bead.md @@ -347,9 +347,10 @@ mechanism. ``` **Status mapping.** The bd CLI uses six statuses (open, in_progress, -blocked, review, testing, closed). `mapBdStatus()` in -`internal/beads/bdstore.go` collapses these to Gas City's three: closed -maps to closed, in_progress maps to in_progress, everything else to open. +blocked, review, testing, closed), but the `beads.Store` contract stays +three-state: open, in_progress, closed. `BdStore` therefore maps bd's +blocked/review/testing values to open. An empty status from a backend is +also normalized to open. ## Code Map diff --git a/engdocs/contributors/huma-usage.md b/engdocs/contributors/huma-usage.md new file mode 100644 index 0000000000..b8ab77058f --- /dev/null +++ b/engdocs/contributors/huma-usage.md @@ -0,0 +1,346 @@ +--- +title: "Huma Usage Notes" +description: "Contributor notes for Huma v2 patterns, quirks, and generated OpenAPI behavior in Gas City's HTTP and SSE API." +--- + +Gas City's HTTP + SSE control plane is built on Huma v2 +(`github.com/danielgtaylor/huma/v2`). This document captures the +framework-level patterns and quirks we've learned the hard way, so +future contributors can find the answer here instead of re-deriving +it from the framework source or stumbling into the same traps. + +Every pattern below is load-bearing in the current implementation; +removing one breaks a specific invariant described in +[API Control Plane](../architecture/api-control-plane.md). + +## 1. Presence detection for query parameters + +**Problem.** Some endpoints need to distinguish "query parameter +absent" from "query parameter present with empty/zero value." The +naive answer — a pointer field like `Cursor *string` — panics at +registration because Huma v2 does not support pointer query +parameters (`huma.go:189`, issue +[#288](https://github.com/danielgtaylor/huma/issues/288)). + +**Solution.** Use the `OptionalParam[T]` wrapper. Gas City's copy +lives at `internal/api/huma_optional_param.go`: + +```go +type OptionalParam[T any] struct { + Value T + IsSet bool +} + +func (o OptionalParam[T]) Schema(r huma.Registry) *huma.Schema { + return huma.SchemaFromType(r, reflect.TypeOf(o.Value)) +} + +func (o *OptionalParam[T]) Receiver() reflect.Value { + return reflect.ValueOf(o).Elem().Field(0) +} + +func (o *OptionalParam[T]) OnParamSet(isSet bool, _ any) { + o.IsSet = isSet +} +``` + +Declared on an input struct: + +```go +type SessionListInput struct { + CityScope + Cursor OptionalParam[string] `query:"cursor"` +} +``` + +`Schema()` emits the wrapped `T`'s schema unchanged, so the wire +contract is identical to a plain `query:"cursor" string` field. +`IsSet` is populated by `OnParamSet`, which Huma's binder calls +after parsing. + +**The sharp edge.** Huma's parameter binder treats empty string +values as `isSet = false` (`huma.go:881-882`: +`isSet = value != ""`). `?cursor=` and no `cursor=` at all are +indistinguishable at the handler. Three-state semantics (absent / +present-empty / present-nonempty) are not expressible under Huma; +APIs must design around two-state (`IsSet && value != ""`). + +This constraint is intrinsic to the framework, not a Gas City +choice. Do not work around it by reading raw URL values in a +Resolver (that's an [API Control Plane](../architecture/api-control-plane.md) +§3.5.1 violation). + +## 2. Pointer query params panic; Resolvers cannot rescue them + +Huma checks the kind of every query/header/path/cookie field at +registration (`huma.go:189`): + +```go +if f.Type.Kind() == reflect.Pointer { + panic("pointers are not supported for form/header/path/query parameters") +} +``` + +The TODO in the source acknowledges this is solvable but hasn't +been done. Until it is, every optional query parameter must be a +value type plus either `OptionalParam[T]` (for presence detection) +or a sentinel value the handler interprets. + +Resolvers (`huma.Resolver` / `huma.ResolverWithPath`) can validate +or normalize values the struct already declares, but they MUST NOT +read keys off `ctx.URL().Query()` or `ctx.Header()` that are not +declared fields. Hidden contract, §3.5.1 violation. + +## 3. Operation handlers — Huma's escape hatch for cross-cutting +ops-level metadata + +`huma.Get`/`Post`/`Put`/`Patch`/`Delete` take `operationHandlers +...func(op *Operation)` after the handler. They run AFTER Huma has +auto-populated `OperationID` / `Summary` / `Method` / `Path`, so +the handler can append to `op.Parameters` (or `op.Responses`, +`op.Tags`, etc.) without disturbing the auto-generated identity. + +Gas City uses this to declare the `X-GC-Request` CSRF header +without touching 50+ input structs. In +`internal/api/city_scope.go`: + +```go +func addMutationCSRFParam(op *huma.Operation) { + // idempotent append of the X-GC-Request header param +} + +func cityPost[I, O any] (sm *SupervisorMux, tail string, + fn func(*Server, context.Context, *I) (*O, error), +) { + huma.Post(sm.humaAPI, cityScopePrefix+tail, bindCity(sm, fn), + addMutationCSRFParam) +} +``` + +**The sharp edge.** If you skip `huma.Post` and call +`huma.Register` with a manually-built `huma.Operation`, you lose +auto-`OperationID`/Summary generation — the spec ends up with +empty `operationId` fields, and oapi-codegen falls back to +path-synthesized method names (`PostV0CityCityNameRigNameAction` +instead of `PostV0CityByCityNameRigByNameByAction`). The +regenerated Go client fails to compile against existing call +sites. Lesson: use the convenience helpers, not `huma.Register` +directly, whenever the auto-generated metadata is acceptable. + +## 4. OperationID generation + +`huma.go:2192`: + +```go +var GenerateOperationID = func(method, path string, response any) string { + // ... produces kebab-case IDs like "post-v0-city-by-city-name-rig-by-name-by-action" + return casing.Kebab(action + "-" + + reRemoveIDs.ReplaceAllString(path, "by-$1")) +} +``` + +`{param}` segments are replaced with `by-param`. The kebab ID +becomes the operationId in the OpenAPI spec, which oapi-codegen +PascalCases into Go method names. Explicit `OperationID` values +(e.g. `"create-agent"`) are preserved. + +Skipping `huma.Post` and registering via `huma.Register` directly +bypasses this generator. If you see a mutation endpoint in +`openapi.json` with no `operationId`, you're looking at a case +where someone built the Operation by hand and forgot to populate +`OperationID`. + +## 5. Response headers that apply to every operation + +OpenAPI 3.1 has no "global response header" mechanism. The +canonical pattern is to declare the header once in +`components.headers` and `$ref` it from each operation's +responses: + +```yaml +components: + headers: + X-Request-Id: + description: ... + schema: {type: string} + +paths: + /foo: + get: + responses: + "200": + headers: + X-Request-Id: + $ref: "#/components/headers/X-Request-Id" +``` + +Gas City does this in `internal/api/huma_spec_framework.go`: +`registerFrameworkHeaders` runs once after all routes are +registered, populates `api.OpenAPI().Components.Headers`, and +walks every operation's responses to inject `{Ref: "#/..."}` +entries. One named component backs 284 `$ref` entries, so the +spec stays readable and generated clients get a single typed +accessor. + +`huma.Header` is a type alias for `huma.Param` +(`openapi.go:588`), which is why `Response.Headers` is typed +`map[string]*Param`. Setting `Ref: "#/components/headers/NAME"` +on the Param produces the `$ref` emission in the generated YAML. + +## 6. Per-operation custom response headers (SSE streams) + +Some SSE endpoints emit custom headers like `GC-Agent-Status` +or `GC-Session-State` via `hctx.SetHeader`. These are not +global — they apply to specific streams — so they belong on +the operation's 200 response, not in +`components.headers`. But inlining the description at the +registration site means 3+ copies of the same description +string. + +Gas City's pattern (`internal/api/sse.go`): + +```go +var sseStatusHeaders = map[string]string{ + "GC-Agent-Status": "...", + "GC-Session-State": "...", + "GC-Session-Status": "...", +} + +func sseResponseHeaders(names ...string) map[string]*huma.Response { + // builds Responses["200"].Headers from the catalog; + // panics if a name is not in sseStatusHeaders. +} +``` + +Registration site: + +```go +registerSSE(sm.humaAPI, huma.Operation{ + OperationID: "stream-agent-output", + ... + Responses: sseResponseHeaders("GC-Agent-Status"), +}, ...) +``` + +The panic-on-unknown-name is load-bearing: it makes drift +between `hctx.SetHeader` call sites and the declared contract +surface at startup, not at "why isn't my client getting this +header" debug time. + +## 7. Middleware ordering vs request validation + +Middleware registered via `api.UseMiddleware` wraps the whole +request path; it runs BEFORE Huma's parameter validation. That's +why `humaCSRFMiddleware` can return `403 csrf: ...` when +`X-GC-Request` is missing — by the time Huma's validator sees +the request, the middleware has already short-circuited. + +Without the middleware, Huma's own validator would return +`422 Unprocessable Entity` with `"required header parameter is +missing"` for the same case. Both reject, but 403 is the +semantically correct status for CSRF rejection and is more +scannable in logs. + +**Implication.** A spec that declares a header `required:true` +must have a mechanism (middleware, handler code, Huma validator) +that actually enforces it. The spec describes the contract; it +does not enforce it. `TestGeneratedClientInSync` catches spec +drift but not enforcement drift. + +## 8. Convenience path auto-metadata — `_convenience_id` sentinel + +When `huma.Post` et al. run, they set `op.OperationID` to the +auto-generated kebab-case ID, then run user operation handlers, +then check: + +```go +if operation.OperationID == opID { + operation.Metadata["_convenience_id"] = opID + operation.Metadata["_convenience_id_out"] = o +} +``` + +The metadata sentinel lets `huma.Group` regenerate the ID if the +operation gets moved under a prefix. If you want to override the +auto-ID, do it inside an operation handler: + +```go +huma.Post(api, "/things", handler, func(op *huma.Operation) { + op.OperationID = "create-thing" +}) +``` + +The same trick applies to `Summary`. + +## 9. SSE: three hand-written zones around typed payloads + +`internal/api/sse.go` is the single sanctioned location for SSE +protocol framing. It hand-writes `id:` / `event:` / `data:` / +blank-line separators around a call to `encoder.Encode(payload)` +where payload is a typed, schema-registered struct. This is the +§3.4 carve-out described in the +[API Control Plane](../architecture/api-control-plane.md). + +Three related hand-written helpers: + +1. `beginSSEStream(hctx)` — sets `Content-Type: text/event-stream`, + `Cache-Control: no-cache`, `Connection: keep-alive` via + `hctx.SetHeader`, then returns the body writer, JSON encoder, + and Flusher. +2. `writeSSEFrame(...)` — emits one frame: optional `id:` line, + `event:` line from the type→event reverse lookup, `data:` + line via `encoder.Encode(data)`, blank line, flush. +3. `attachSSEResponseSchema(...)` — populates + `op.Responses["200"].Content["text/event-stream"]` with a + `oneOf` schema listing every event variant. Called before + `huma.Register` so the spec describes the stream's event + shapes. + +Third-party SSE adapters (e.g. Huma's built-in `sse.Register`) +do not support precheck errors; an op like `stream-events` that +needs to 503 before committing stream headers has to use our +`registerSSE` wrapper instead. + +## 10. When to reach for `CreateHooks = nil` + +Huma's default `Config` installs a `SchemaLinkTransformer` that +adds `$schema` properties and `Link` response headers. Gas City +disables this with `cfg.CreateHooks = nil` in +`huma_handlers_supervisor.go:newSupervisorHumaAPI`. Reason: we +don't serve the schema at the Link target, and the extra header +clutters generated clients. + +If you need other CreateHooks, don't nil the slice — append your +hook to the default list. + +## 11. Adapter import path + +`humago` lives at +`github.com/danielgtaylor/huma/v2/adapters/humago`, not at +`github.com/danielgtaylor/huma/v2/humago`. The README examples in +some Huma versions are out of date on this; `pkg.go.dev` has the +real path. + +```go +import "github.com/danielgtaylor/huma/v2/adapters/humago" +``` + +## What we don't use from Huma + +- **`huma.Group`** — we have a single API per supervisor and a + per-city handler dispatcher. Group's prefix/middleware + composition would duplicate what `cityRegister` already does. +- **Huma's built-in `sse.Register`** — cannot return HTTP errors + before stream headers commit; we use our own `registerSSE` for + that reason (§9 above). +- **`huma.OperationTags`** convenience — we set `Tags` directly on + the Operation literal where present. + +## Upstream issues we're tracking + +- **#288** — pointer query params panic. Blocks the cleanest form + of optional parameters; `OptionalParam[T]` is the documented + workaround and will remain the idiom here even after a fix. +- Response-header "global declaration" ergonomics — OpenAPI 3.1 + limitation, not a Huma gap. Our `registerFrameworkHeaders` is + the `$ref` pattern applied programmatically. diff --git a/engdocs/contributors/index.md b/engdocs/contributors/index.md index 38fe7ad999..a28236b841 100644 --- a/engdocs/contributors/index.md +++ b/engdocs/contributors/index.md @@ -9,6 +9,8 @@ description: The shortest path for new contributors to get productive in Gas Cit - [Architecture Overview](../architecture/index.md) - [Primitive Test](primitive-test.md) - [Reconciler Debugging](reconciler-debugging.md) +- [Huma Usage Notes](huma-usage.md) when touching `internal/api/`, + OpenAPI generation, or SSE registration - [`CONTRIBUTING.md`](https://github.com/gastownhall/gascity/blob/main/CONTRIBUTING.md) - [`TESTING.md`](https://github.com/gastownhall/gascity/blob/main/TESTING.md) diff --git a/examples/bd/assets/scripts/gc-beads-bd.sh b/examples/bd/assets/scripts/gc-beads-bd.sh index 69d3639b14..56cb7b79df 100755 --- a/examples/bd/assets/scripts/gc-beads-bd.sh +++ b/examples/bd/assets/scripts/gc-beads-bd.sh @@ -1511,6 +1511,17 @@ op_start() { log_offset=$(wc -c < "$LOG_FILE" 2>/dev/null || echo 0) fi + # Disable Dolt's load-average auto-GC scheduler. Dolt 1.86.0+ + # ships a loadAvgGCScheduler whose threshold formula scales + # inversely with CPU count (10/CPUs), so on multi-core hosts the + # gate is essentially always tripped and CALL DOLT_GC() is + # queued but never executed; auto_gc_behavior.enable: true in + # config.yaml has no effect. See + # https://github.com/dolthub/dolt/issues/10944. Users who + # explicitly set DOLT_GC_SCHEDULER are respected. + : "${DOLT_GC_SCHEDULER=NONE}" + export DOLT_GC_SCHEDULER + # Start dolt sql-server with config file. Close the startup lock fd in # the child so the flock is released when this starter exits. nohup sh -c 'exec 9>&-; exec dolt sql-server --config "$1"' sh "$CONFIG_FILE" >> "$LOG_FILE" 2>&1 & diff --git a/examples/dolt/commands/gc-nudge/run.sh b/examples/dolt/commands/gc-nudge/run.sh index 37af4e0d8c..302c988d3e 100755 --- a/examples/dolt/commands/gc-nudge/run.sh +++ b/examples/dolt/commands/gc-nudge/run.sh @@ -1,16 +1,20 @@ #!/bin/sh -# gc dolt gc-nudge — Size-triggered CALL DOLT_GC() to compact a bloated -# Dolt database. +# gc dolt gc-nudge — periodic CALL DOLT_GC() to bound the Dolt commit graph. # -# Why this exists: Dolt's auto-GC (default-on in 1.75+) fires on *growth* -# — 125 MB delta since last GC. A database that bloated once and then -# stabilized never auto-GCs on its own. This command closes that corner: -# it checks disk size on each registered rig's Dolt database, and if any -# are above the configured threshold, issues CALL DOLT_GC() against the -# managed sql-server. +# Why this exists: Gas City's managed-Dolt launch path now forces +# `DOLT_GC_SCHEDULER=NONE` to work around +# https://github.com/dolthub/dolt/issues/10944, so threshold-triggered +# auto-GC can fire again on multi-core hosts. We still keep an hourly +# nudge because the bd workload can accumulate history quickly, and an +# unconditional `CALL DOLT_GC()` remains a cheap belt-and-suspenders +# backstop for reclaiming orphan chunks before they turn into disk bloat +# and tail-latency spikes. # -# Runs from the dolt pack's dolt-gc-nudge order on a slow cooldown (6h by -# default). Intended to be idempotent and cheap when nothing needs GC. +# Policy: fire CALL DOLT_GC() unconditionally on every cooldown tick +# (default 1h). The GC is idempotent and near-free when there's nothing +# to reclaim. A threshold knob remains as an optional escape hatch. +# +# Runs from the dolt pack's dolt-gc-nudge order. # # Environment: # GC_CITY_PATH (required) — city root @@ -19,8 +23,9 @@ # GC_DOLT_USER (default: root) # GC_DOLT_PASSWORD (optional) # GC_DOLT_GC_THRESHOLD_BYTES -# (default: 2147483648 = 2 GiB) — minimum .dolt/ size that triggers GC. -# Set to 0 to force GC on every tick (useful for tests). +# (default: 0 — run unconditionally). Set a positive byte count to +# skip GC on databases below that size; useful for test suites that +# don't want GC noise on tiny fixtures. # GC_DOLT_GC_CALL_TIMEOUT_SECS # (default: 1800) — wall-clock bound for one `CALL DOLT_GC()` invocation. # GC_DOLT_GC_DRY_RUN (optional) — when set, prints what would happen @@ -90,7 +95,7 @@ fi : "${GC_DOLT_USER:=root}" host="${GC_DOLT_HOST:-127.0.0.1}" -threshold="${GC_DOLT_GC_THRESHOLD_BYTES:-2147483648}" +threshold="${GC_DOLT_GC_THRESHOLD_BYTES:-0}" gc_call_timeout="${GC_DOLT_GC_CALL_TIMEOUT_SECS:-1800}" dry_run="${GC_DOLT_GC_DRY_RUN:-}" @@ -291,7 +296,8 @@ run_dolt_gc_for_db() { run_bounded "$gc_call_timeout" \ dolt --host "$host" --port "$GC_DOLT_PORT" \ --user "$GC_DOLT_USER" --no-tls \ - sql --database "$db" -q "CALL DOLT_GC()" || cmd_rc=$? + --use-db "$db" \ + sql -q "CALL DOLT_GC()" || cmd_rc=$? elapsed=$(( $(date +%s) - start )) after=$(dir_bytes "$db_dir") diff --git a/examples/dolt/commands/health/run.sh b/examples/dolt/commands/health/run.sh index 3e3385b67b..4490dffb44 100755 --- a/examples/dolt/commands/health/run.sh +++ b/examples/dolt/commands/health/run.sh @@ -235,6 +235,9 @@ fi # positives from processes that merely mention "dolt" in their args # (e.g., Claude sessions whose prompt text contains "dolt sql-server"). # +# Rig-local Dolt servers (configured via dolt.port in config.yaml) +# are legitimate — exclude any PID listening on a known rig port. +# # GC_HEALTH_SKIP_ZOMBIE_SCAN is a test-only escape hatch. Zombie # enumeration spawns one `ps` per matching process, which on shared # dev machines with many accumulated dolt processes dominates the @@ -244,8 +247,22 @@ fi zombie_count=0 zombie_pids="" if [ "${GC_HEALTH_SKIP_ZOMBIE_SCAN:-0}" != "1" ]; then + # Collect PIDs of legitimate rig-local Dolt servers. + rig_dolt_pids="" + while IFS= read -r meta; do + [ -f "$meta" ] || continue + config_file="$(dirname "$meta")/config.yaml" + [ -f "$config_file" ] || continue + rig_port=$(grep '^dolt\.port:' "$config_file" 2>/dev/null | sed "s/^dolt\\.port:[[:space:]]*//; s/[[:space:]]*#.*$//; s/['\\\"]//g; s/[[:space:]]*$//" | head -1) + case "$rig_port" in ''|*[!0-9]*) continue ;; esac + [ "$rig_port" = "$GC_DOLT_PORT" ] && continue + rig_pid=$(managed_runtime_listener_pid "$rig_port" || true) + [ -n "$rig_pid" ] && rig_dolt_pids="$rig_dolt_pids $rig_pid " + done < "$_meta_cache" + for p in $(pgrep -x dolt 2>/dev/null || true); do [ "$p" = "$server_pid" ] && continue + case "$rig_dolt_pids" in *" $p "*) continue ;; esac cmd=$(ps -p "$p" -o args= 2>/dev/null || true) case "$cmd" in *sql-server*) ;; diff --git a/examples/dolt/health_test.go b/examples/dolt/health_test.go index f72e3a0a51..5c7addf3fc 100644 --- a/examples/dolt/health_test.go +++ b/examples/dolt/health_test.go @@ -688,6 +688,141 @@ func writeExecutable(t *testing.T, path, contents string) { } } +// TestHealthScriptZombieScanExcludesRigLocalServers verifies that +// Dolt processes on rig-configured ports are not flagged as zombies. +// Regression guard for the bug where deacon patrol killed rig-local +// Dolt servers because the zombie scan treated every non-city-server +// dolt sql-server PID as a zombie. +func runHealthScriptZombieScanExcludesRigLocalServers(t *testing.T, rigConfig string) { + cityPath := t.TempDir() + fakeBin := t.TempDir() + + mainPort := "19901" + rigPort := "19902" + + mainPID := "424201" + rigPID := "424202" + zombiePID := "424203" + + // City .beads directory with metadata. + if err := os.MkdirAll(filepath.Join(cityPath, ".beads"), 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(cityPath, ".beads", "metadata.json"), + []byte(`{"dolt_database":"city"}`), 0o644); err != nil { + t.Fatal(err) + } + + // Rig directory with config.yaml containing dolt.port. + rigBeads := filepath.Join(cityPath, "rigs", "enterprise", ".beads") + if err := os.MkdirAll(rigBeads, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(rigBeads, "metadata.json"), + []byte(`{"dolt_database":"enterprise"}`), 0o644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(rigBeads, "config.yaml"), + []byte(rigConfig), 0o644); err != nil { + t.Fatal(err) + } + + // Fake gc: fail so metadata_files() falls back to find. + writeExecutable(t, filepath.Join(fakeBin, "gc"), "#!/bin/sh\nexit 1\n") + + // Fake pgrep: returns rig PID and zombie PID (main PID excluded + // by server_pid check, not by pgrep filtering). + writeExecutable(t, filepath.Join(fakeBin, "pgrep"), + fmt.Sprintf("#!/bin/sh\necho %s\necho %s\necho %s\n", mainPID, rigPID, zombiePID)) + + // Fake lsof: maps ports to PIDs. + writeExecutable(t, filepath.Join(fakeBin, "lsof"), + fmt.Sprintf(`#!/bin/sh +for arg in "$@"; do + case "$arg" in + -iTCP:%s) echo %s; exit 0 ;; + -iTCP:%s) echo %s; exit 0 ;; + esac +done +exit 1 +`, mainPort, mainPID, rigPort, rigPID)) + + // Fake ps: handles pid_is_running (-o pid=) and zombie scan (-o args=). + writeExecutable(t, filepath.Join(fakeBin, "ps"), `#!/bin/sh +if [ "$1" = "-p" ] && [ "$3" = "-o" ]; then + case "$4" in + pid=) printf ' %s\n' "$2"; exit 0 ;; + args=) echo "dolt sql-server"; exit 0 ;; + esac +fi +exit 1 +`) + + // Fake nc: unreachable (no real server). + writeExecutable(t, filepath.Join(fakeBin, "nc"), "#!/bin/sh\nexit 1\n") + + // Fake dolt: SELECT 1 fails (no real server). + writeExecutable(t, filepath.Join(fakeBin, "dolt"), "#!/bin/sh\nexit 1\n") + + root := repoRoot(t) + cmd := exec.Command("sh", filepath.Join(root, healthScript), "--json") + cmd.Env = append( + filteredEnv("GC_CITY_PATH", "GC_PACK_DIR", "GC_DOLT_HOST", "GC_DOLT_PORT", + "GC_DOLT_USER", "GC_DOLT_PASSWORD", "GC_HEALTH_SKIP_ZOMBIE_SCAN", "PATH"), + "GC_CITY_PATH="+cityPath, + "GC_PACK_DIR="+root, + "GC_DOLT_HOST=127.0.0.1", + "GC_DOLT_PORT="+mainPort, + "GC_DOLT_USER=root", + "GC_DOLT_PASSWORD=", + "PATH="+fakeBin+string(os.PathListSeparator)+os.Getenv("PATH"), + ) + + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("health.sh failed: %v\n%s", err, out) + } + + output := string(out) + + // The true zombie (424203) should be counted. + if !strings.Contains(output, `"zombie_count": 1`) { + t.Errorf("expected zombie_count 1; got:\n%s", output) + } + + // The rig PID (424202) must NOT appear in zombie_pids. + if strings.Contains(output, rigPID) { + t.Errorf("rig-local Dolt PID %s should not be in zombie_pids; got:\n%s", rigPID, output) + } + + // The true zombie PID (424203) must appear in zombie_pids. + if !strings.Contains(output, zombiePID) { + t.Errorf("true zombie PID %s should be in zombie_pids; got:\n%s", zombiePID, output) + } +} + +func TestHealthScriptZombieScanExcludesRigLocalServers(t *testing.T) { + tests := []struct { + name string + rigConfig string + }{ + { + name: "bare port", + rigConfig: "dolt.port: 19902\n", + }, + { + name: "quoted port", + rigConfig: "dolt.port: \"19902\"\n", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + runHealthScriptZombieScanExcludesRigLocalServers(t, tc.rigConfig) + }) + } +} + // TestHealthScriptJSONAlwaysExitsZero guards the JSON-mode exit // contract. Automation consumers (notably the deacon patrol formula) // parse the JSON payload and key health decisions off `server.reachable`. diff --git a/examples/dolt/orders/dolt-gc-nudge.toml b/examples/dolt/orders/dolt-gc-nudge.toml index 297f938dd1..a44b78c638 100644 --- a/examples/dolt/orders/dolt-gc-nudge.toml +++ b/examples/dolt/orders/dolt-gc-nudge.toml @@ -1,6 +1,6 @@ [order] -description = "Size-triggered CALL DOLT_GC() when a Dolt database grows past threshold" +description = "Periodic CALL DOLT_GC() to bound commit-graph size (Dolt's auto-GC doesn't fire on bd workloads)" trigger = "cooldown" -interval = "6h" +interval = "1h" exec = "gc dolt gc-nudge" timeout = "24h" diff --git a/internal/api/city_scope.go b/internal/api/city_scope.go index 65dacb7f4c..23d5eb4d9e 100644 --- a/internal/api/city_scope.go +++ b/internal/api/city_scope.go @@ -78,6 +78,49 @@ func bindCity[I any, O any]( } } +// csrfHeaderName is the anti-CSRF header required on every mutation +// request. Any non-empty value satisfies the check; the header's +// presence is what matters, because cross-origin XHR from an attacker +// origin cannot set custom request headers without triggering a CORS +// preflight the API does not grant. See OWASP's "Use of Custom Request +// Headers" defense. +const csrfHeaderName = "X-GC-Request" + +// csrfHeaderDescription is the shared description used for the header +// in generated OpenAPI specs so the spec and runtime enforcement agree. +const csrfHeaderDescription = "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks." + +// addMutationCSRFParam is an operationHandler (see huma.Post et al.) +// that appends the X-GC-Request required header parameter to op. +// Mutation-verb registration helpers pass this handler so the spec +// describes the middleware's enforcement rather than advertising a +// false "no special headers needed" contract. +// +// The header is declared once per mutation operation (OpenAPI 3.1 has +// no mechanism for global per-verb parameters; see +// speakeasy.com/openapi/responses/headers). Idempotent so handlers +// whose input struct happens to declare the header explicitly are not +// double-registered. +func addMutationCSRFParam(op *huma.Operation) { + for _, p := range op.Parameters { + if p != nil && p.In == "header" && p.Name == csrfHeaderName { + return + } + } + minLen := 1 + op.Parameters = append(op.Parameters, &huma.Param{ + Name: csrfHeaderName, + In: "header", + Required: true, + Description: csrfHeaderDescription, + Schema: &huma.Schema{ + Type: "string", + MinLength: &minLen, + Description: csrfHeaderDescription, + }, + }) +} + // cityGet registers a per-city GET op at /v0/city/{cityName}+tail. // The tail starts with "/" (e.g. "/agents") or is "" for the // city-detail base path. @@ -87,41 +130,51 @@ func cityGet[I any, O any](sm *SupervisorMux, tail string, huma.Get(sm.humaAPI, cityScopePrefix+tail, bindCity(sm, fn)) } -// cityPost is the POST sibling of cityGet. +// cityPost is the POST sibling of cityGet. Every city-scoped POST +// mutation flows through this helper, so declaring the X-GC-Request +// header param here covers every current and future mutation without +// per-input-struct boilerplate. func cityPost[I any, O any](sm *SupervisorMux, tail string, fn func(*Server, context.Context, *I) (*O, error), ) { - huma.Post(sm.humaAPI, cityScopePrefix+tail, bindCity(sm, fn)) + huma.Post(sm.humaAPI, cityScopePrefix+tail, bindCity(sm, fn), addMutationCSRFParam) } -// cityPut is the PUT sibling of cityGet. +// cityPut is the PUT sibling of cityGet. See cityPost for the CSRF +// header rationale. func cityPut[I any, O any](sm *SupervisorMux, tail string, fn func(*Server, context.Context, *I) (*O, error), ) { - huma.Put(sm.humaAPI, cityScopePrefix+tail, bindCity(sm, fn)) + huma.Put(sm.humaAPI, cityScopePrefix+tail, bindCity(sm, fn), addMutationCSRFParam) } -// cityPatch is the PATCH sibling of cityGet. +// cityPatch is the PATCH sibling of cityGet. See cityPost for the CSRF +// header rationale. func cityPatch[I any, O any](sm *SupervisorMux, tail string, fn func(*Server, context.Context, *I) (*O, error), ) { - huma.Patch(sm.humaAPI, cityScopePrefix+tail, bindCity(sm, fn)) + huma.Patch(sm.humaAPI, cityScopePrefix+tail, bindCity(sm, fn), addMutationCSRFParam) } -// cityDelete is the DELETE sibling of cityGet. +// cityDelete is the DELETE sibling of cityGet. See cityPost for the +// CSRF header rationale. func cityDelete[I any, O any](sm *SupervisorMux, tail string, fn func(*Server, context.Context, *I) (*O, error), ) { - huma.Delete(sm.humaAPI, cityScopePrefix+tail, bindCity(sm, fn)) + huma.Delete(sm.humaAPI, cityScopePrefix+tail, bindCity(sm, fn), addMutationCSRFParam) } // cityRegister is the per-city analog of huma.Register. Use it when // the op needs explicit OperationID, DefaultStatus, Summary, etc. -// op.Path is the tail after /v0/city/{cityName}. +// op.Path is the tail after /v0/city/{cityName}. CSRF-header declaration +// is applied automatically for mutation verbs. func cityRegister[I any, O any](sm *SupervisorMux, op huma.Operation, fn func(*Server, context.Context, *I) (*O, error), ) { op.Path = cityScopePrefix + op.Path + if isMutationMethod(op.Method) { + addMutationCSRFParam(&op) + } huma.Register(sm.humaAPI, op, bindCity(sm, fn)) } diff --git a/internal/api/client.go b/internal/api/client.go index e38f771faf..1ac31b07ac 100644 --- a/internal/api/client.go +++ b/internal/api/client.go @@ -244,7 +244,7 @@ func (c *Client) RestartService(name string) error { if err := c.requireCityScope(); err != nil { return err } - resp, err := c.cw.PostV0CityByCityNameServiceByNameRestartWithResponse(context.Background(), c.cityName, name) + resp, err := c.cw.PostV0CityByCityNameServiceByNameRestartWithResponse(context.Background(), c.cityName, name, nil) return checkMutation(resp, err) } @@ -258,7 +258,7 @@ func (c *Client) patchCity(suspend bool) error { if err := c.requireCityScope(); err != nil { return err } - resp, err := c.cw.PatchV0CityByCityNameWithResponse(context.Background(), c.cityName, genclient.PatchV0CityByCityNameJSONRequestBody{Suspended: &suspend}) + resp, err := c.cw.PatchV0CityByCityNameWithResponse(context.Background(), c.cityName, nil, genclient.PatchV0CityByCityNameJSONRequestBody{Suspended: &suspend}) return checkMutation(resp, err) } @@ -285,12 +285,12 @@ func (c *Client) postAgentAction(name, action string) error { if dir, base, ok := strings.Cut(name, "/"); ok { resp, err := c.cw.PostV0CityByCityNameAgentByDirByBaseByActionWithResponse( context.Background(), c.cityName, dir, base, - genclient.PostV0CityByCityNameAgentByDirByBaseByActionParamsAction(action)) + genclient.PostV0CityByCityNameAgentByDirByBaseByActionParamsAction(action), nil) return checkMutation(resp, err) } resp, err := c.cw.PostV0CityByCityNameAgentByBaseByActionWithResponse( context.Background(), c.cityName, name, - genclient.PostV0CityByCityNameAgentByBaseByActionParamsAction(action)) + genclient.PostV0CityByCityNameAgentByBaseByActionParamsAction(action), nil) return checkMutation(resp, err) } @@ -308,7 +308,7 @@ func (c *Client) postRigAction(name, action string) error { if err := c.requireCityScope(); err != nil { return err } - resp, err := c.cw.PostV0CityByCityNameRigByNameByActionWithResponse(context.Background(), c.cityName, name, action) + resp, err := c.cw.PostV0CityByCityNameRigByNameByActionWithResponse(context.Background(), c.cityName, name, action, nil) return checkMutation(resp, err) } @@ -317,7 +317,7 @@ func (c *Client) KillSession(id string) error { if err := c.requireCityScope(); err != nil { return err } - resp, err := c.cw.PostV0CityByCityNameSessionByIdKillWithResponse(context.Background(), c.cityName, id) + resp, err := c.cw.PostV0CityByCityNameSessionByIdKillWithResponse(context.Background(), c.cityName, id, nil) return checkMutation(resp, err) } @@ -329,7 +329,7 @@ func (c *Client) SendSessionMessage(id, message string) error { } ctx, cancel := context.WithTimeout(context.Background(), sessionMessageTimeout) defer cancel() - resp, err := c.cw.SendSessionMessageWithResponse(ctx, c.cityName, id, genclient.SendSessionMessageJSONRequestBody{ + resp, err := c.cw.SendSessionMessageWithResponse(ctx, c.cityName, id, nil, genclient.SendSessionMessageJSONRequestBody{ Message: message, }) return checkMutation(resp, err) @@ -346,7 +346,7 @@ func (c *Client) SubmitSession(id, message string, intent session.SubmitIntent) i := genclient.SubmitIntent(intent) body.Intent = &i } - resp, err := c.cw.SubmitSessionWithResponse(context.Background(), c.cityName, id, body) + resp, err := c.cw.SubmitSessionWithResponse(context.Background(), c.cityName, id, nil, body) if err != nil { return SessionSubmitResponse{}, &connError{err: fmt.Errorf("request failed: %w", err)} } diff --git a/internal/api/convoy_event_stream_test.go b/internal/api/convoy_event_stream_test.go index 0cbb931fc2..9aca4436fa 100644 --- a/internal/api/convoy_event_stream_test.go +++ b/internal/api/convoy_event_stream_test.go @@ -2,6 +2,7 @@ package api import ( "encoding/json" + "reflect" "testing" "time" @@ -9,6 +10,29 @@ import ( "github.com/gastownhall/gascity/internal/events" ) +func TestCityLifecycleEventsSharePayloadTypeForOneOfValidation(t *testing.T) { + registered := events.RegisteredPayloadTypes() + cityEvents := []string{ + events.CityCreated, + events.CityReady, + events.CityInitFailed, + events.CityUnregisterRequested, + events.CityUnregistered, + events.CityUnregisterFailed, + } + + firstType := reflect.TypeOf(registered[cityEvents[0]]) + if firstType == nil { + t.Fatalf("%s payload type is not registered", cityEvents[0]) + } + for _, eventType := range cityEvents[1:] { + got := reflect.TypeOf(registered[eventType]) + if got != firstType { + t.Fatalf("%s payload type = %v, want shared %v so EventPayload oneOf has a single city lifecycle branch", eventType, got, firstType) + } + } +} + func TestWorkflowEventScope(t *testing.T) { info := workflowStoreInfo{ref: "rig:alpha", scopeKind: "rig", scopeRef: "alpha"} diff --git a/internal/api/event_envelope_schemas.go b/internal/api/event_envelope_schemas.go new file mode 100644 index 0000000000..d40848ba69 --- /dev/null +++ b/internal/api/event_envelope_schemas.go @@ -0,0 +1,162 @@ +package api + +import ( + "reflect" + "sort" + "strings" + + "github.com/danielgtaylor/huma/v2" + "github.com/gastownhall/gascity/internal/events" +) + +type typedEventStreamEnvelopeSchema struct{} + +func (typedEventStreamEnvelopeSchema) Schema(r huma.Registry) *huma.Schema { + return registerTypedEventEnvelopeSchema(r, typedEventEnvelopeSchemaConfig{ + name: "TypedEventStreamEnvelope", + title: "Typed city event stream envelope", + description: "Discriminated union of city event stream envelopes. Each variant constrains the envelope type and payload schema together.", + }) +} + +type typedTaggedEventStreamEnvelopeSchema struct{} + +func (typedTaggedEventStreamEnvelopeSchema) Schema(r huma.Registry) *huma.Schema { + return registerTypedEventEnvelopeSchema(r, typedEventEnvelopeSchemaConfig{ + name: "TypedTaggedEventStreamEnvelope", + title: "Typed supervisor event stream envelope", + description: "Discriminated union of supervisor event stream envelopes. Each variant constrains the envelope type and payload schema together and includes the source city.", + includeCity: true, + }) +} + +type typedEventEnvelopeSchemaConfig struct { + name string + title string + description string + includeCity bool +} + +type typedEventEnvelopeVariant struct { + eventType string + payloadType reflect.Type +} + +func registerEventEnvelopeCompatibilitySchemas(r huma.Registry) { + r.Schema(reflect.TypeOf(eventStreamEnvelope{}), true, "EventStreamEnvelope") + r.Schema(reflect.TypeOf(taggedEventStreamEnvelope{}), true, "TaggedEventStreamEnvelope") + _ = typedEventStreamEnvelopeSchema{}.Schema(r) + _ = typedTaggedEventStreamEnvelopeSchema{}.Schema(r) +} + +func registerTypedEventEnvelopeSchema(r huma.Registry, cfg typedEventEnvelopeSchemaConfig) *huma.Schema { + if _, ok := r.Map()[cfg.name]; !ok { + variants := typedEventEnvelopeVariants() + oneOf := make([]*huma.Schema, 0, len(variants)) + mapping := make(map[string]string, len(variants)) + for _, variant := range variants { + variantName := cfg.name + eventTypeSchemaSuffix(variant.eventType) + ref := schemaRefPrefix + variantName + if _, ok := r.Map()[variantName]; !ok { + r.Map()[variantName] = typedEventEnvelopeVariantSchema(r, variant, cfg) + } + oneOf = append(oneOf, &huma.Schema{Ref: ref}) + mapping[variant.eventType] = ref + } + r.Map()[cfg.name] = &huma.Schema{ + Title: cfg.title, + Description: cfg.description, + OneOf: oneOf, + Discriminator: &huma.Discriminator{ + PropertyName: "type", + Mapping: mapping, + }, + } + } + return &huma.Schema{Ref: schemaRefPrefix + cfg.name} +} + +func typedEventEnvelopeVariants() []typedEventEnvelopeVariant { + registered := events.RegisteredPayloadTypes() + variants := make([]typedEventEnvelopeVariant, 0, len(events.KnownEventTypes)) + for _, eventType := range events.KnownEventTypes { + payload, ok := registered[eventType] + if !ok { + panic("api: known event type has no registered payload: " + eventType) + } + payloadType := reflect.TypeOf(payload) + if payloadType == nil { + panic("api: known event type has nil registered payload: " + eventType) + } + variants = append(variants, typedEventEnvelopeVariant{ + eventType: eventType, + payloadType: payloadType, + }) + } + sort.Slice(variants, func(i, j int) bool { + return variants[i].eventType < variants[j].eventType + }) + return variants +} + +func typedEventEnvelopeVariantSchema(r huma.Registry, variant typedEventEnvelopeVariant, cfg typedEventEnvelopeSchemaConfig) *huma.Schema { + properties := map[string]*huma.Schema{ + "seq": { + Type: huma.TypeInteger, + Format: "int64", + Minimum: float64Ptr(0), + }, + "type": { + Type: huma.TypeString, + Extensions: map[string]any{ + "const": variant.eventType, + }, + }, + "ts": { + Type: huma.TypeString, + Format: "date-time", + }, + "actor": { + Type: huma.TypeString, + }, + "subject": { + Type: huma.TypeString, + }, + "message": { + Type: huma.TypeString, + }, + "workflow": r.Schema(reflect.TypeOf(workflowEventProjection{}), true, "WorkflowEventProjection"), + "payload": r.Schema(variant.payloadType, true, variant.payloadType.Name()), + } + required := []string{"seq", "type", "ts", "actor", "payload"} + if cfg.includeCity { + properties["city"] = &huma.Schema{Type: huma.TypeString} + required = append(required, "city") + } + return &huma.Schema{ + Title: cfg.name + " " + variant.eventType, + Type: huma.TypeObject, + AdditionalProperties: false, + Properties: properties, + Required: required, + } +} + +func eventTypeSchemaSuffix(eventType string) string { + parts := strings.FieldsFunc(eventType, func(r rune) bool { + return r == '.' || r == '_' || r == '-' + }) + var out strings.Builder + for _, part := range parts { + if part == "" { + continue + } + out.WriteString(strings.ToUpper(part[:1])) + out.WriteString(part[1:]) + } + return out.String() +} + +func float64Ptr(v float64) *float64 { + return &v +} diff --git a/internal/api/event_payloads.go b/internal/api/event_payloads.go index 8c5485c636..fffb9755e3 100644 --- a/internal/api/event_payloads.go +++ b/internal/api/event_payloads.go @@ -28,6 +28,41 @@ type MailEventPayload struct { // IsEventPayload marks MailEventPayload as an events.Payload variant. func (MailEventPayload) IsEventPayload() {} +// CityLifecyclePayload is emitted by city lifecycle events. Keeping all +// same-shaped city lifecycle payloads on one Go type keeps the generated +// EventPayload oneOf unambiguous for validators that only see the payload +// object, not the enclosing event type. +type CityLifecyclePayload struct { + Name string `json:"name"` + Path string `json:"path"` + Error string `json:"error,omitempty"` + PhasesCompleted []string `json:"phases_completed,omitempty"` +} + +// IsEventPayload marks CityLifecyclePayload as an events.Payload variant. +func (CityLifecyclePayload) IsEventPayload() {} + +// CityCreatedPayload is emitted on city.created when the supervisor's +// POST /v0/city handler has scaffolded and registered a new city. +type CityCreatedPayload = CityLifecyclePayload + +// CityReadyPayload is emitted on city.ready when the supervisor +// reconciler has finished preparing a city. +type CityReadyPayload = CityLifecyclePayload + +// CityInitFailedPayload is emitted on city.init_failed when the +// supervisor reconciler fails to bring up a city. +type CityInitFailedPayload = CityLifecyclePayload + +// CityUnregisterRequestedPayload is emitted when unregister starts. +type CityUnregisterRequestedPayload = CityLifecyclePayload + +// CityUnregisteredPayload is emitted when unregister completes. +type CityUnregisteredPayload = CityLifecyclePayload + +// CityUnregisterFailedPayload is emitted when unregister fails. +type CityUnregisterFailedPayload = CityLifecyclePayload + // BeadEventPayload is the shape of every bead.* event payload // (BeadCreated, BeadUpdated, BeadClosed). The payload carries a full // snapshot of the bead as of the event; it is emitted by the beads @@ -97,9 +132,15 @@ func init() { events.RegisterPayload(events.ControllerStopped, events.NoPayload{}) events.RegisterPayload(events.CitySuspended, events.NoPayload{}) events.RegisterPayload(events.CityResumed, events.NoPayload{}) + events.RegisterPayload(events.CityCreated, CityCreatedPayload{}) + events.RegisterPayload(events.CityReady, CityReadyPayload{}) + events.RegisterPayload(events.CityInitFailed, CityInitFailedPayload{}) events.RegisterPayload(events.OrderFired, events.NoPayload{}) events.RegisterPayload(events.OrderCompleted, events.NoPayload{}) events.RegisterPayload(events.OrderFailed, events.NoPayload{}) events.RegisterPayload(events.ProviderSwapped, events.NoPayload{}) events.RegisterPayload(events.WorkerOperation, WorkerOperationEventPayload{}) + events.RegisterPayload(events.CityUnregisterRequested, CityUnregisterRequestedPayload{}) + events.RegisterPayload(events.CityUnregistered, CityUnregisteredPayload{}) + events.RegisterPayload(events.CityUnregisterFailed, CityUnregisterFailedPayload{}) } diff --git a/internal/api/fake_state_test.go b/internal/api/fake_state_test.go index 431d817f59..47d01c2bc7 100644 --- a/internal/api/fake_state_test.go +++ b/internal/api/fake_state_test.go @@ -302,10 +302,17 @@ func (f *fakeMutatorState) UpdateProvider(name string, patch ProviderUpdate) err if patch.Command != nil { spec.Command = *patch.Command } + if patch.ACPCommand != nil { + spec.ACPCommand = *patch.ACPCommand + } if patch.Args != nil { spec.Args = make([]string, len(patch.Args)) copy(spec.Args, patch.Args) } + if patch.ACPArgs != nil { + spec.ACPArgs = make([]string, len(patch.ACPArgs)) + copy(spec.ACPArgs, patch.ACPArgs) + } if patch.ArgsAppend != nil { spec.ArgsAppend = make([]string, len(patch.ArgsAppend)) copy(spec.ArgsAppend, patch.ArgsAppend) diff --git a/internal/api/genclient/client_gen.go b/internal/api/genclient/client_gen.go index aca06d0ab9..2b1a71518a 100644 --- a/internal/api/genclient/client_gen.go +++ b/internal/api/genclient/client_gen.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -371,6 +372,8 @@ type AnnotatedAgentResponse struct { // AnnotatedProviderResponse defines model for AnnotatedProviderResponse. type AnnotatedProviderResponse struct { + AcpArgs *[]string `json:"acp_args,omitempty"` + AcpCommand *string `json:"acp_command,omitempty"` Args *[]string `json:"args,omitempty"` Command *string `json:"command,omitempty"` DisplayName *string `json:"display_name,omitempty"` @@ -419,6 +422,12 @@ type BeadCreateInputBody struct { // Labels Bead labels. Labels *[]string `json:"labels,omitempty"` + // Metadata Metadata key-value pairs to set at create time. + Metadata *map[string]string `json:"metadata,omitempty"` + + // Parent Parent bead ID. + Parent *string `json:"parent,omitempty"` + // Priority Bead priority. Priority *int64 `json:"priority,omitempty"` @@ -463,6 +472,9 @@ type BeadUpdateBody struct { // Metadata Metadata key-value pairs to set. Metadata *map[string]string `json:"metadata,omitempty"` + // Parent Parent bead ID. Use null or an empty string to clear. + Parent *string `json:"parent,omitempty"` + // Priority Bead priority. Priority *int64 `json:"priority,omitempty"` @@ -506,10 +518,13 @@ type CityCreateRequestBootstrapProfile string // CityCreateResponse defines model for CityCreateResponse. type CityCreateResponse struct { - // Ok True on success. + // Name Resolved city name as persisted in city.toml. Use this to filter the event stream for completion. + Name string `json:"name"` + + // Ok True when scaffolding + registration succeeded. Does not imply the city is ready yet; watch /v0/events/stream for city.ready. Ok bool `json:"ok"` - // Path Resolved absolute path of the created city. + // Path Resolved absolute path of the created city directory. Path string `json:"path"` } @@ -536,12 +551,32 @@ type CityInfo struct { Status *string `json:"status,omitempty"` } +// CityLifecyclePayload defines model for CityLifecyclePayload. +type CityLifecyclePayload struct { + Error *string `json:"error,omitempty"` + Name string `json:"name"` + Path string `json:"path"` + PhasesCompleted *[]string `json:"phases_completed,omitempty"` +} + // CityPatchInputBody defines model for CityPatchInputBody. type CityPatchInputBody struct { // Suspended Whether the city is suspended. Suspended *bool `json:"suspended,omitempty"` } +// CityUnregisterResponse defines model for CityUnregisterResponse. +type CityUnregisterResponse struct { + // Name Resolved registry name. Filter the event stream by this to observe completion. + Name string `json:"name"` + + // Ok True when the registry entry was removed and the supervisor was signaled. Does not imply the city's controller has stopped yet; watch /v0/events/stream for city.unregistered. + Ok bool `json:"ok"` + + // Path Resolved absolute city directory. The directory itself is not modified; unregister only affects the supervisor's registry. + Path string `json:"path"` +} + // ConfigAgentResponse defines model for ConfigAgentResponse. type ConfigAgentResponse struct { Dir *string `json:"dir,omitempty"` @@ -1726,6 +1761,12 @@ type PoolOverride struct { // ProviderCreateInputBody defines model for ProviderCreateInputBody. type ProviderCreateInputBody struct { + // AcpArgs ACP transport command arguments override. + AcpArgs *[]string `json:"acp_args,omitempty"` + + // AcpCommand ACP transport command binary override. + AcpCommand *string `json:"acp_command,omitempty"` + // Args Command arguments. Args *[]string `json:"args,omitempty"` @@ -1780,6 +1821,8 @@ type ProviderOptionDTO struct { // ProviderPatch defines model for ProviderPatch. type ProviderPatch struct { + ACPArgs *[]string `json:"ACPArgs"` + ACPCommand *string `json:"ACPCommand"` Args *[]string `json:"Args"` ArgsAppend *[]string `json:"ArgsAppend"` Base *string `json:"Base"` @@ -1796,6 +1839,12 @@ type ProviderPatch struct { // ProviderPatchSetInputBody defines model for ProviderPatchSetInputBody. type ProviderPatchSetInputBody struct { + // AcpArgs Override ACP transport command arguments. + AcpArgs *[]string `json:"acp_args,omitempty"` + + // AcpCommand Override ACP transport command binary. + AcpCommand *string `json:"acp_command,omitempty"` + // Args Override command arguments. Args *[]string `json:"args,omitempty"` @@ -1854,6 +1903,8 @@ type ProviderReadinessResponse struct { // ProviderResponse defines model for ProviderResponse. type ProviderResponse struct { + AcpArgs *[]string `json:"acp_args,omitempty"` + AcpCommand *string `json:"acp_command,omitempty"` Args *[]string `json:"args,omitempty"` Builtin bool `json:"builtin"` CityLevel bool `json:"city_level"` @@ -1868,6 +1919,8 @@ type ProviderResponse struct { // ProviderSpecJSON defines model for ProviderSpecJSON. type ProviderSpecJSON struct { + AcpArgs *[]string `json:"acp_args,omitempty"` + AcpCommand *string `json:"acp_command,omitempty"` Args *[]string `json:"args,omitempty"` Command *string `json:"command,omitempty"` DisplayName *string `json:"display_name,omitempty"` @@ -1879,6 +1932,12 @@ type ProviderSpecJSON struct { // ProviderUpdateInputBody defines model for ProviderUpdateInputBody. type ProviderUpdateInputBody struct { + // AcpArgs ACP transport command arguments override. + AcpArgs *[]string `json:"acp_args,omitempty"` + + // AcpCommand ACP transport command binary override. + AcpCommand *string `json:"acp_command,omitempty"` + // Args Command arguments. Args *[]string `json:"args,omitempty"` @@ -2197,6 +2256,11 @@ type SessionResponse struct { Title string `json:"title"` } +// SessionStreamCommonEvent Non-message events emitted on the session SSE stream: activity transitions, pending interactions, and keepalive heartbeats. The concrete variant is identified by the SSE event name. +type SessionStreamCommonEvent struct { + union json.RawMessage +} + // SessionStreamMessageEvent defines model for SessionStreamMessageEvent. type SessionStreamMessageEvent struct { Format string `json:"format"` @@ -2273,6 +2337,9 @@ type SlingInputBody struct { // Bead Bead ID to sling. Bead *string `json:"bead,omitempty"` + // Force Bypass cross-rig guards; for direct bead routes, also bypass missing-bead validation. Formula-backed graph routes may replace existing live workflow roots but still require the source bead to exist. + Force *bool `json:"force,omitempty"` + // Formula Formula name for workflow launch. Formula *string `json:"formula,omitempty"` @@ -2477,795 +2544,4902 @@ type TranscriptMessageKind string // TranscriptProvenance Provenance of a transcript entry (freshly observed vs. replayed from persisted history). type TranscriptProvenance string -// UnboundEventPayload defines model for UnboundEventPayload. -type UnboundEventPayload struct { - Count int64 `json:"count"` - SessionId string `json:"session_id"` +// TypedEventStreamEnvelope Discriminated union of city event stream envelopes. Each variant constrains the envelope type and payload schema together. +type TypedEventStreamEnvelope struct { + union json.RawMessage } -// WireEvent defines model for WireEvent. -type WireEvent struct { - Actor string `json:"actor"` - Message *string `json:"message,omitempty"` - Payload *EventPayload `json:"payload,omitempty"` - Seq int64 `json:"seq"` - Subject *string `json:"subject,omitempty"` - Ts time.Time `json:"ts"` - Type string `json:"type"` +// TypedEventStreamEnvelopeBeadClosed defines model for TypedEventStreamEnvelopeBeadClosed. +type TypedEventStreamEnvelopeBeadClosed struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload BeadEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` } -// WireTaggedEvent defines model for WireTaggedEvent. -type WireTaggedEvent struct { - Actor string `json:"actor"` - City string `json:"city"` - Message *string `json:"message,omitempty"` - Payload *EventPayload `json:"payload,omitempty"` - Seq int64 `json:"seq"` - Subject *string `json:"subject,omitempty"` - Ts time.Time `json:"ts"` - Type string `json:"type"` +// TypedEventStreamEnvelopeBeadCreated defines model for TypedEventStreamEnvelopeBeadCreated. +type TypedEventStreamEnvelopeBeadCreated struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload BeadEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` } -// WorkerOperationEventPayload defines model for WorkerOperationEventPayload. -type WorkerOperationEventPayload struct { - Delivered *bool `json:"delivered,omitempty"` - DurationMs int64 `json:"duration_ms"` - Error *string `json:"error,omitempty"` - FinishedAt time.Time `json:"finished_at"` - OpId string `json:"op_id"` - Operation string `json:"operation"` - Provider *string `json:"provider,omitempty"` - Queued *bool `json:"queued,omitempty"` - Result string `json:"result"` - SessionId *string `json:"session_id,omitempty"` - SessionName *string `json:"session_name,omitempty"` - StartedAt time.Time `json:"started_at"` - Template *string `json:"template,omitempty"` - Transport *string `json:"transport,omitempty"` +// TypedEventStreamEnvelopeBeadUpdated defines model for TypedEventStreamEnvelopeBeadUpdated. +type TypedEventStreamEnvelopeBeadUpdated struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload BeadEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` } -// WorkflowAttemptSummary defines model for WorkflowAttemptSummary. -type WorkflowAttemptSummary struct { - ActiveAttempt int64 `json:"active_attempt"` - AttemptCount int64 `json:"attempt_count"` - MaxAttempts *int64 `json:"max_attempts,omitempty"` +// TypedEventStreamEnvelopeCityCreated defines model for TypedEventStreamEnvelopeCityCreated. +type TypedEventStreamEnvelopeCityCreated struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload CityLifecyclePayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` } -// WorkflowBeadResponse defines model for WorkflowBeadResponse. -type WorkflowBeadResponse struct { - Assignee *string `json:"assignee,omitempty"` - Attempt *int64 `json:"attempt,omitempty"` - Id string `json:"id"` - Kind string `json:"kind"` - LogicalBeadId *string `json:"logical_bead_id,omitempty"` - Metadata map[string]string `json:"metadata"` - ScopeRef *string `json:"scope_ref,omitempty"` - Status string `json:"status"` - StepRef *string `json:"step_ref,omitempty"` - Title string `json:"title"` +// TypedEventStreamEnvelopeCityInitFailed defines model for TypedEventStreamEnvelopeCityInitFailed. +type TypedEventStreamEnvelopeCityInitFailed struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload CityLifecyclePayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` } -// WorkflowDeleteResponse defines model for WorkflowDeleteResponse. -type WorkflowDeleteResponse struct { - // Closed Number of beads closed. - Closed int64 `json:"closed"` - - // Deleted Number of beads deleted. - Deleted int64 `json:"deleted"` - - // Partial True when one or more teardown steps failed; Closed/Deleted still reflect what succeeded. - Partial *bool `json:"partial,omitempty"` +// TypedEventStreamEnvelopeCityReady defines model for TypedEventStreamEnvelopeCityReady. +type TypedEventStreamEnvelopeCityReady struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload CityLifecyclePayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} - // PartialErrors Human-readable errors from failed teardown steps. - PartialErrors *[]string `json:"partial_errors,omitempty"` +// TypedEventStreamEnvelopeCityResumed defines model for TypedEventStreamEnvelopeCityResumed. +type TypedEventStreamEnvelopeCityResumed struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} - // WorkflowId Workflow ID. - WorkflowId string `json:"workflow_id"` +// TypedEventStreamEnvelopeCitySuspended defines model for TypedEventStreamEnvelopeCitySuspended. +type TypedEventStreamEnvelopeCitySuspended struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` } -// WorkflowDepResponse defines model for WorkflowDepResponse. -type WorkflowDepResponse struct { - From string `json:"from"` - Kind *string `json:"kind,omitempty"` - To string `json:"to"` +// TypedEventStreamEnvelopeCityUnregisterFailed defines model for TypedEventStreamEnvelopeCityUnregisterFailed. +type TypedEventStreamEnvelopeCityUnregisterFailed struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload CityLifecyclePayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` } -// WorkflowEventProjection defines model for WorkflowEventProjection. -type WorkflowEventProjection struct { - AttemptSummary *WorkflowAttemptSummary `json:"attempt_summary,omitempty"` - Bead WorkflowBeadResponse `json:"bead"` - ChangedFields *[]string `json:"changed_fields"` - EventSeq int64 `json:"event_seq"` - EventTs string `json:"event_ts"` - EventType string `json:"event_type"` - LogicalNodeId string `json:"logical_node_id"` - RequiresResync *bool `json:"requires_resync,omitempty"` - RootBeadId string `json:"root_bead_id"` - RootStoreRef string `json:"root_store_ref"` - ScopeKind string `json:"scope_kind"` - ScopeRef string `json:"scope_ref"` - Type string `json:"type"` - WatchGeneration string `json:"watch_generation"` - WorkflowId string `json:"workflow_id"` - WorkflowSeq int64 `json:"workflow_seq"` +// TypedEventStreamEnvelopeCityUnregisterRequested defines model for TypedEventStreamEnvelopeCityUnregisterRequested. +type TypedEventStreamEnvelopeCityUnregisterRequested struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload CityLifecyclePayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` } -// WorkflowSnapshotResponse defines model for WorkflowSnapshotResponse. -type WorkflowSnapshotResponse struct { - Beads *[]WorkflowBeadResponse `json:"beads"` - Deps *[]WorkflowDepResponse `json:"deps"` - LogicalEdges *[]WorkflowDepResponse `json:"logical_edges"` - LogicalNodes *[]LogicalNode `json:"logical_nodes"` - Partial bool `json:"partial"` - ResolvedRootStore string `json:"resolved_root_store"` - RootBeadId string `json:"root_bead_id"` - RootStoreRef string `json:"root_store_ref"` - ScopeGroups *[]ScopeGroup `json:"scope_groups"` - ScopeKind string `json:"scope_kind"` - ScopeRef string `json:"scope_ref"` - SnapshotEventSeq *int64 `json:"snapshot_event_seq,omitempty"` - SnapshotVersion int64 `json:"snapshot_version"` - StoresScanned *[]string `json:"stores_scanned"` - WorkflowId string `json:"workflow_id"` +// TypedEventStreamEnvelopeCityUnregistered defines model for TypedEventStreamEnvelopeCityUnregistered. +type TypedEventStreamEnvelopeCityUnregistered struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload CityLifecyclePayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` } -// WorkspaceResponse defines model for WorkspaceResponse. -type WorkspaceResponse struct { - DeclaredName *string `json:"declared_name,omitempty"` - DeclaredPrefix *string `json:"declared_prefix,omitempty"` - Name string `json:"name"` - Prefix *string `json:"prefix,omitempty"` - Provider *string `json:"provider,omitempty"` - SessionTemplate *string `json:"session_template,omitempty"` - Suspended bool `json:"suspended"` +// TypedEventStreamEnvelopeControllerStarted defines model for TypedEventStreamEnvelopeControllerStarted. +type TypedEventStreamEnvelopeControllerStarted struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` } -// GetV0CityByCityNameAgentByBaseOutputParams defines parameters for GetV0CityByCityNameAgentByBaseOutput. -type GetV0CityByCityNameAgentByBaseOutputParams struct { - // Tail Number of recent compaction segments to return. This API parameter keeps compaction-segment semantics even though gc session logs --tail counts displayed transcript entries. Omit for the endpoint default (usually 1); 0 returns all segments; N>0 returns the last N. - Tail *string `form:"tail,omitempty" json:"tail,omitempty"` +// TypedEventStreamEnvelopeControllerStopped defines model for TypedEventStreamEnvelopeControllerStopped. +type TypedEventStreamEnvelopeControllerStopped struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} - // Before Message UUID cursor for loading older messages. - Before *string `form:"before,omitempty" json:"before,omitempty"` +// TypedEventStreamEnvelopeConvoyClosed defines model for TypedEventStreamEnvelopeConvoyClosed. +type TypedEventStreamEnvelopeConvoyClosed struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeConvoyCreated defines model for TypedEventStreamEnvelopeConvoyCreated. +type TypedEventStreamEnvelopeConvoyCreated struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeExtmsgAdapterAdded defines model for TypedEventStreamEnvelopeExtmsgAdapterAdded. +type TypedEventStreamEnvelopeExtmsgAdapterAdded struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload AdapterEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeExtmsgAdapterRemoved defines model for TypedEventStreamEnvelopeExtmsgAdapterRemoved. +type TypedEventStreamEnvelopeExtmsgAdapterRemoved struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload AdapterEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeExtmsgBound defines model for TypedEventStreamEnvelopeExtmsgBound. +type TypedEventStreamEnvelopeExtmsgBound struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload BoundEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeExtmsgGroupCreated defines model for TypedEventStreamEnvelopeExtmsgGroupCreated. +type TypedEventStreamEnvelopeExtmsgGroupCreated struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload GroupCreatedEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeExtmsgInbound defines model for TypedEventStreamEnvelopeExtmsgInbound. +type TypedEventStreamEnvelopeExtmsgInbound struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload InboundEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeExtmsgOutbound defines model for TypedEventStreamEnvelopeExtmsgOutbound. +type TypedEventStreamEnvelopeExtmsgOutbound struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload OutboundEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeExtmsgUnbound defines model for TypedEventStreamEnvelopeExtmsgUnbound. +type TypedEventStreamEnvelopeExtmsgUnbound struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload UnboundEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeMailArchived defines model for TypedEventStreamEnvelopeMailArchived. +type TypedEventStreamEnvelopeMailArchived struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeMailDeleted defines model for TypedEventStreamEnvelopeMailDeleted. +type TypedEventStreamEnvelopeMailDeleted struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeMailMarkedRead defines model for TypedEventStreamEnvelopeMailMarkedRead. +type TypedEventStreamEnvelopeMailMarkedRead struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeMailMarkedUnread defines model for TypedEventStreamEnvelopeMailMarkedUnread. +type TypedEventStreamEnvelopeMailMarkedUnread struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeMailRead defines model for TypedEventStreamEnvelopeMailRead. +type TypedEventStreamEnvelopeMailRead struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeMailReplied defines model for TypedEventStreamEnvelopeMailReplied. +type TypedEventStreamEnvelopeMailReplied struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeMailSent defines model for TypedEventStreamEnvelopeMailSent. +type TypedEventStreamEnvelopeMailSent struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeOrderCompleted defines model for TypedEventStreamEnvelopeOrderCompleted. +type TypedEventStreamEnvelopeOrderCompleted struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeOrderFailed defines model for TypedEventStreamEnvelopeOrderFailed. +type TypedEventStreamEnvelopeOrderFailed struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeOrderFired defines model for TypedEventStreamEnvelopeOrderFired. +type TypedEventStreamEnvelopeOrderFired struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeProviderSwapped defines model for TypedEventStreamEnvelopeProviderSwapped. +type TypedEventStreamEnvelopeProviderSwapped struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeSessionCrashed defines model for TypedEventStreamEnvelopeSessionCrashed. +type TypedEventStreamEnvelopeSessionCrashed struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeSessionDraining defines model for TypedEventStreamEnvelopeSessionDraining. +type TypedEventStreamEnvelopeSessionDraining struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeSessionIdleKilled defines model for TypedEventStreamEnvelopeSessionIdleKilled. +type TypedEventStreamEnvelopeSessionIdleKilled struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeSessionQuarantined defines model for TypedEventStreamEnvelopeSessionQuarantined. +type TypedEventStreamEnvelopeSessionQuarantined struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeSessionStopped defines model for TypedEventStreamEnvelopeSessionStopped. +type TypedEventStreamEnvelopeSessionStopped struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeSessionSuspended defines model for TypedEventStreamEnvelopeSessionSuspended. +type TypedEventStreamEnvelopeSessionSuspended struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeSessionUndrained defines model for TypedEventStreamEnvelopeSessionUndrained. +type TypedEventStreamEnvelopeSessionUndrained struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeSessionUpdated defines model for TypedEventStreamEnvelopeSessionUpdated. +type TypedEventStreamEnvelopeSessionUpdated struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeSessionWoke defines model for TypedEventStreamEnvelopeSessionWoke. +type TypedEventStreamEnvelopeSessionWoke struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedEventStreamEnvelopeWorkerOperation defines model for TypedEventStreamEnvelopeWorkerOperation. +type TypedEventStreamEnvelopeWorkerOperation struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload WorkerOperationEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelope Discriminated union of supervisor event stream envelopes. Each variant constrains the envelope type and payload schema together and includes the source city. +type TypedTaggedEventStreamEnvelope struct { + union json.RawMessage +} + +// TypedTaggedEventStreamEnvelopeBeadClosed defines model for TypedTaggedEventStreamEnvelopeBeadClosed. +type TypedTaggedEventStreamEnvelopeBeadClosed struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload BeadEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeBeadCreated defines model for TypedTaggedEventStreamEnvelopeBeadCreated. +type TypedTaggedEventStreamEnvelopeBeadCreated struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload BeadEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeBeadUpdated defines model for TypedTaggedEventStreamEnvelopeBeadUpdated. +type TypedTaggedEventStreamEnvelopeBeadUpdated struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload BeadEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeCityCreated defines model for TypedTaggedEventStreamEnvelopeCityCreated. +type TypedTaggedEventStreamEnvelopeCityCreated struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload CityLifecyclePayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeCityInitFailed defines model for TypedTaggedEventStreamEnvelopeCityInitFailed. +type TypedTaggedEventStreamEnvelopeCityInitFailed struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload CityLifecyclePayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeCityReady defines model for TypedTaggedEventStreamEnvelopeCityReady. +type TypedTaggedEventStreamEnvelopeCityReady struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload CityLifecyclePayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeCityResumed defines model for TypedTaggedEventStreamEnvelopeCityResumed. +type TypedTaggedEventStreamEnvelopeCityResumed struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeCitySuspended defines model for TypedTaggedEventStreamEnvelopeCitySuspended. +type TypedTaggedEventStreamEnvelopeCitySuspended struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeCityUnregisterFailed defines model for TypedTaggedEventStreamEnvelopeCityUnregisterFailed. +type TypedTaggedEventStreamEnvelopeCityUnregisterFailed struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload CityLifecyclePayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeCityUnregisterRequested defines model for TypedTaggedEventStreamEnvelopeCityUnregisterRequested. +type TypedTaggedEventStreamEnvelopeCityUnregisterRequested struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload CityLifecyclePayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeCityUnregistered defines model for TypedTaggedEventStreamEnvelopeCityUnregistered. +type TypedTaggedEventStreamEnvelopeCityUnregistered struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload CityLifecyclePayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeControllerStarted defines model for TypedTaggedEventStreamEnvelopeControllerStarted. +type TypedTaggedEventStreamEnvelopeControllerStarted struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeControllerStopped defines model for TypedTaggedEventStreamEnvelopeControllerStopped. +type TypedTaggedEventStreamEnvelopeControllerStopped struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeConvoyClosed defines model for TypedTaggedEventStreamEnvelopeConvoyClosed. +type TypedTaggedEventStreamEnvelopeConvoyClosed struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeConvoyCreated defines model for TypedTaggedEventStreamEnvelopeConvoyCreated. +type TypedTaggedEventStreamEnvelopeConvoyCreated struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded defines model for TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded. +type TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload AdapterEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved defines model for TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved. +type TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload AdapterEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeExtmsgBound defines model for TypedTaggedEventStreamEnvelopeExtmsgBound. +type TypedTaggedEventStreamEnvelopeExtmsgBound struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload BoundEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeExtmsgGroupCreated defines model for TypedTaggedEventStreamEnvelopeExtmsgGroupCreated. +type TypedTaggedEventStreamEnvelopeExtmsgGroupCreated struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload GroupCreatedEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeExtmsgInbound defines model for TypedTaggedEventStreamEnvelopeExtmsgInbound. +type TypedTaggedEventStreamEnvelopeExtmsgInbound struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload InboundEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeExtmsgOutbound defines model for TypedTaggedEventStreamEnvelopeExtmsgOutbound. +type TypedTaggedEventStreamEnvelopeExtmsgOutbound struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload OutboundEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeExtmsgUnbound defines model for TypedTaggedEventStreamEnvelopeExtmsgUnbound. +type TypedTaggedEventStreamEnvelopeExtmsgUnbound struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload UnboundEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeMailArchived defines model for TypedTaggedEventStreamEnvelopeMailArchived. +type TypedTaggedEventStreamEnvelopeMailArchived struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeMailDeleted defines model for TypedTaggedEventStreamEnvelopeMailDeleted. +type TypedTaggedEventStreamEnvelopeMailDeleted struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeMailMarkedRead defines model for TypedTaggedEventStreamEnvelopeMailMarkedRead. +type TypedTaggedEventStreamEnvelopeMailMarkedRead struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeMailMarkedUnread defines model for TypedTaggedEventStreamEnvelopeMailMarkedUnread. +type TypedTaggedEventStreamEnvelopeMailMarkedUnread struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeMailRead defines model for TypedTaggedEventStreamEnvelopeMailRead. +type TypedTaggedEventStreamEnvelopeMailRead struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeMailReplied defines model for TypedTaggedEventStreamEnvelopeMailReplied. +type TypedTaggedEventStreamEnvelopeMailReplied struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeMailSent defines model for TypedTaggedEventStreamEnvelopeMailSent. +type TypedTaggedEventStreamEnvelopeMailSent struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload MailEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeOrderCompleted defines model for TypedTaggedEventStreamEnvelopeOrderCompleted. +type TypedTaggedEventStreamEnvelopeOrderCompleted struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeOrderFailed defines model for TypedTaggedEventStreamEnvelopeOrderFailed. +type TypedTaggedEventStreamEnvelopeOrderFailed struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeOrderFired defines model for TypedTaggedEventStreamEnvelopeOrderFired. +type TypedTaggedEventStreamEnvelopeOrderFired struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeProviderSwapped defines model for TypedTaggedEventStreamEnvelopeProviderSwapped. +type TypedTaggedEventStreamEnvelopeProviderSwapped struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeSessionCrashed defines model for TypedTaggedEventStreamEnvelopeSessionCrashed. +type TypedTaggedEventStreamEnvelopeSessionCrashed struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeSessionDraining defines model for TypedTaggedEventStreamEnvelopeSessionDraining. +type TypedTaggedEventStreamEnvelopeSessionDraining struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeSessionIdleKilled defines model for TypedTaggedEventStreamEnvelopeSessionIdleKilled. +type TypedTaggedEventStreamEnvelopeSessionIdleKilled struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeSessionQuarantined defines model for TypedTaggedEventStreamEnvelopeSessionQuarantined. +type TypedTaggedEventStreamEnvelopeSessionQuarantined struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeSessionStopped defines model for TypedTaggedEventStreamEnvelopeSessionStopped. +type TypedTaggedEventStreamEnvelopeSessionStopped struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeSessionSuspended defines model for TypedTaggedEventStreamEnvelopeSessionSuspended. +type TypedTaggedEventStreamEnvelopeSessionSuspended struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeSessionUndrained defines model for TypedTaggedEventStreamEnvelopeSessionUndrained. +type TypedTaggedEventStreamEnvelopeSessionUndrained struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeSessionUpdated defines model for TypedTaggedEventStreamEnvelopeSessionUpdated. +type TypedTaggedEventStreamEnvelopeSessionUpdated struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeSessionWoke defines model for TypedTaggedEventStreamEnvelopeSessionWoke. +type TypedTaggedEventStreamEnvelopeSessionWoke struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload NoPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// TypedTaggedEventStreamEnvelopeWorkerOperation defines model for TypedTaggedEventStreamEnvelopeWorkerOperation. +type TypedTaggedEventStreamEnvelopeWorkerOperation struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload WorkerOperationEventPayload `json:"payload"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` + Workflow *WorkflowEventProjection `json:"workflow,omitempty"` +} + +// UnboundEventPayload defines model for UnboundEventPayload. +type UnboundEventPayload struct { + Count int64 `json:"count"` + SessionId string `json:"session_id"` +} + +// WireEvent defines model for WireEvent. +type WireEvent struct { + Actor string `json:"actor"` + Message *string `json:"message,omitempty"` + Payload *EventPayload `json:"payload,omitempty"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` +} + +// WireTaggedEvent defines model for WireTaggedEvent. +type WireTaggedEvent struct { + Actor string `json:"actor"` + City string `json:"city"` + Message *string `json:"message,omitempty"` + Payload *EventPayload `json:"payload,omitempty"` + Seq int64 `json:"seq"` + Subject *string `json:"subject,omitempty"` + Ts time.Time `json:"ts"` + Type string `json:"type"` +} + +// WorkerOperationEventPayload defines model for WorkerOperationEventPayload. +type WorkerOperationEventPayload struct { + Delivered *bool `json:"delivered,omitempty"` + DurationMs int64 `json:"duration_ms"` + Error *string `json:"error,omitempty"` + FinishedAt time.Time `json:"finished_at"` + OpId string `json:"op_id"` + Operation string `json:"operation"` + Provider *string `json:"provider,omitempty"` + Queued *bool `json:"queued,omitempty"` + Result string `json:"result"` + SessionId *string `json:"session_id,omitempty"` + SessionName *string `json:"session_name,omitempty"` + StartedAt time.Time `json:"started_at"` + Template *string `json:"template,omitempty"` + Transport *string `json:"transport,omitempty"` +} + +// WorkflowAttemptSummary defines model for WorkflowAttemptSummary. +type WorkflowAttemptSummary struct { + ActiveAttempt int64 `json:"active_attempt"` + AttemptCount int64 `json:"attempt_count"` + MaxAttempts *int64 `json:"max_attempts,omitempty"` +} + +// WorkflowBeadResponse defines model for WorkflowBeadResponse. +type WorkflowBeadResponse struct { + Assignee *string `json:"assignee,omitempty"` + Attempt *int64 `json:"attempt,omitempty"` + Id string `json:"id"` + Kind string `json:"kind"` + LogicalBeadId *string `json:"logical_bead_id,omitempty"` + Metadata map[string]string `json:"metadata"` + ScopeRef *string `json:"scope_ref,omitempty"` + Status string `json:"status"` + StepRef *string `json:"step_ref,omitempty"` + Title string `json:"title"` +} + +// WorkflowDeleteResponse defines model for WorkflowDeleteResponse. +type WorkflowDeleteResponse struct { + // Closed Number of beads closed. + Closed int64 `json:"closed"` + + // Deleted Number of beads deleted. + Deleted int64 `json:"deleted"` + + // Partial True when one or more teardown steps failed; Closed/Deleted still reflect what succeeded. + Partial *bool `json:"partial,omitempty"` + + // PartialErrors Human-readable errors from failed teardown steps. + PartialErrors *[]string `json:"partial_errors,omitempty"` + + // WorkflowId Workflow ID. + WorkflowId string `json:"workflow_id"` +} + +// WorkflowDepResponse defines model for WorkflowDepResponse. +type WorkflowDepResponse struct { + From string `json:"from"` + Kind *string `json:"kind,omitempty"` + To string `json:"to"` +} + +// WorkflowEventProjection defines model for WorkflowEventProjection. +type WorkflowEventProjection struct { + AttemptSummary *WorkflowAttemptSummary `json:"attempt_summary,omitempty"` + Bead WorkflowBeadResponse `json:"bead"` + ChangedFields *[]string `json:"changed_fields"` + EventSeq int64 `json:"event_seq"` + EventTs string `json:"event_ts"` + EventType string `json:"event_type"` + LogicalNodeId string `json:"logical_node_id"` + RequiresResync *bool `json:"requires_resync,omitempty"` + RootBeadId string `json:"root_bead_id"` + RootStoreRef string `json:"root_store_ref"` + ScopeKind string `json:"scope_kind"` + ScopeRef string `json:"scope_ref"` + Type string `json:"type"` + WatchGeneration string `json:"watch_generation"` + WorkflowId string `json:"workflow_id"` + WorkflowSeq int64 `json:"workflow_seq"` +} + +// WorkflowSnapshotResponse defines model for WorkflowSnapshotResponse. +type WorkflowSnapshotResponse struct { + Beads *[]WorkflowBeadResponse `json:"beads"` + Deps *[]WorkflowDepResponse `json:"deps"` + LogicalEdges *[]WorkflowDepResponse `json:"logical_edges"` + LogicalNodes *[]LogicalNode `json:"logical_nodes"` + Partial bool `json:"partial"` + ResolvedRootStore string `json:"resolved_root_store"` + RootBeadId string `json:"root_bead_id"` + RootStoreRef string `json:"root_store_ref"` + ScopeGroups *[]ScopeGroup `json:"scope_groups"` + ScopeKind string `json:"scope_kind"` + ScopeRef string `json:"scope_ref"` + SnapshotEventSeq *int64 `json:"snapshot_event_seq,omitempty"` + SnapshotVersion int64 `json:"snapshot_version"` + StoresScanned *[]string `json:"stores_scanned"` + WorkflowId string `json:"workflow_id"` +} + +// WorkspaceResponse defines model for WorkspaceResponse. +type WorkspaceResponse struct { + DeclaredName *string `json:"declared_name,omitempty"` + DeclaredPrefix *string `json:"declared_prefix,omitempty"` + Name string `json:"name"` + Prefix *string `json:"prefix,omitempty"` + Provider *string `json:"provider,omitempty"` + SessionTemplate *string `json:"session_template,omitempty"` + Suspended bool `json:"suspended"` +} + +// PostV0CityParams defines parameters for PostV0City. +type PostV0CityParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PatchV0CityByCityNameParams defines parameters for PatchV0CityByCityName. +type PatchV0CityByCityNameParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// DeleteV0CityByCityNameAgentByBaseParams defines parameters for DeleteV0CityByCityNameAgentByBase. +type DeleteV0CityByCityNameAgentByBaseParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PatchV0CityByCityNameAgentByBaseParams defines parameters for PatchV0CityByCityNameAgentByBase. +type PatchV0CityByCityNameAgentByBaseParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameAgentByBaseOutputParams defines parameters for GetV0CityByCityNameAgentByBaseOutput. +type GetV0CityByCityNameAgentByBaseOutputParams struct { + // Tail Number of recent compaction segments to return. This API parameter keeps compaction-segment semantics even though gc session logs --tail counts displayed transcript entries. Omit for the endpoint default (usually 1); 0 returns all segments; N>0 returns the last N. + Tail *string `form:"tail,omitempty" json:"tail,omitempty"` + + // Before Message UUID cursor for loading older messages. + Before *string `form:"before,omitempty" json:"before,omitempty"` +} + +// PostV0CityByCityNameAgentByBaseByActionParams defines parameters for PostV0CityByCityNameAgentByBaseByAction. +type PostV0CityByCityNameAgentByBaseByActionParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` } // PostV0CityByCityNameAgentByBaseByActionParamsAction defines parameters for PostV0CityByCityNameAgentByBaseByAction. type PostV0CityByCityNameAgentByBaseByActionParamsAction string -// GetV0CityByCityNameAgentByDirByBaseOutputParams defines parameters for GetV0CityByCityNameAgentByDirByBaseOutput. -type GetV0CityByCityNameAgentByDirByBaseOutputParams struct { - // Tail Number of recent compaction segments to return. This API parameter keeps compaction-segment semantics even though gc session logs --tail counts displayed transcript entries. Omit for the endpoint default (usually 1); 0 returns all segments; N>0 returns the last N. - Tail *string `form:"tail,omitempty" json:"tail,omitempty"` +// DeleteV0CityByCityNameAgentByDirByBaseParams defines parameters for DeleteV0CityByCityNameAgentByDirByBase. +type DeleteV0CityByCityNameAgentByDirByBaseParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PatchV0CityByCityNameAgentByDirByBaseParams defines parameters for PatchV0CityByCityNameAgentByDirByBase. +type PatchV0CityByCityNameAgentByDirByBaseParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameAgentByDirByBaseOutputParams defines parameters for GetV0CityByCityNameAgentByDirByBaseOutput. +type GetV0CityByCityNameAgentByDirByBaseOutputParams struct { + // Tail Number of recent compaction segments to return. This API parameter keeps compaction-segment semantics even though gc session logs --tail counts displayed transcript entries. Omit for the endpoint default (usually 1); 0 returns all segments; N>0 returns the last N. + Tail *string `form:"tail,omitempty" json:"tail,omitempty"` + + // Before Message UUID cursor for loading older messages. + Before *string `form:"before,omitempty" json:"before,omitempty"` +} + +// PostV0CityByCityNameAgentByDirByBaseByActionParams defines parameters for PostV0CityByCityNameAgentByDirByBaseByAction. +type PostV0CityByCityNameAgentByDirByBaseByActionParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameAgentByDirByBaseByActionParamsAction defines parameters for PostV0CityByCityNameAgentByDirByBaseByAction. +type PostV0CityByCityNameAgentByDirByBaseByActionParamsAction string + +// GetV0CityByCityNameAgentsParams defines parameters for GetV0CityByCityNameAgents. +type GetV0CityByCityNameAgentsParams struct { + // Index Event sequence number; when provided, blocks until a newer event arrives. + Index *string `form:"index,omitempty" json:"index,omitempty"` + + // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + Wait *string `form:"wait,omitempty" json:"wait,omitempty"` + + // Pool Filter by pool name. + Pool *string `form:"pool,omitempty" json:"pool,omitempty"` + + // Rig Filter by rig name. + Rig *string `form:"rig,omitempty" json:"rig,omitempty"` + + // Running Filter by running state. Omit to return all agents. + Running *GetV0CityByCityNameAgentsParamsRunning `form:"running,omitempty" json:"running,omitempty"` + + // Peek Include last output preview. + Peek *bool `form:"peek,omitempty" json:"peek,omitempty"` +} + +// GetV0CityByCityNameAgentsParamsRunning defines parameters for GetV0CityByCityNameAgents. +type GetV0CityByCityNameAgentsParamsRunning string + +// CreateAgentParams defines parameters for CreateAgent. +type CreateAgentParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// DeleteV0CityByCityNameBeadByIdParams defines parameters for DeleteV0CityByCityNameBeadById. +type DeleteV0CityByCityNameBeadByIdParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PatchV0CityByCityNameBeadByIdParams defines parameters for PatchV0CityByCityNameBeadById. +type PatchV0CityByCityNameBeadByIdParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameBeadByIdAssignParams defines parameters for PostV0CityByCityNameBeadByIdAssign. +type PostV0CityByCityNameBeadByIdAssignParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameBeadByIdCloseParams defines parameters for PostV0CityByCityNameBeadByIdClose. +type PostV0CityByCityNameBeadByIdCloseParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameBeadByIdReopenParams defines parameters for PostV0CityByCityNameBeadByIdReopen. +type PostV0CityByCityNameBeadByIdReopenParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameBeadByIdUpdateParams defines parameters for PostV0CityByCityNameBeadByIdUpdate. +type PostV0CityByCityNameBeadByIdUpdateParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameBeadsParams defines parameters for GetV0CityByCityNameBeads. +type GetV0CityByCityNameBeadsParams struct { + // Index Event sequence number; when provided, blocks until a newer event arrives. + Index *string `form:"index,omitempty" json:"index,omitempty"` + + // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + Wait *string `form:"wait,omitempty" json:"wait,omitempty"` + + // Cursor Pagination cursor from a previous response's next_cursor field. + Cursor *string `form:"cursor,omitempty" json:"cursor,omitempty"` + + // Limit Maximum number of results to return. 0 = server default. + Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` + + // Status Filter by bead status. + Status *string `form:"status,omitempty" json:"status,omitempty"` + + // Type Filter by bead type. + Type *string `form:"type,omitempty" json:"type,omitempty"` + + // Label Filter by label. + Label *string `form:"label,omitempty" json:"label,omitempty"` + + // Assignee Filter by assignee. + Assignee *string `form:"assignee,omitempty" json:"assignee,omitempty"` + + // Rig Filter by rig. + Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +} + +// CreateBeadParams defines parameters for CreateBead. +type CreateBeadParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` + + // IdempotencyKey Idempotency key for safe retries. + IdempotencyKey *string `json:"Idempotency-Key,omitempty"` +} + +// GetV0CityByCityNameBeadsReadyParams defines parameters for GetV0CityByCityNameBeadsReady. +type GetV0CityByCityNameBeadsReadyParams struct { + // Index Event sequence number; when provided, blocks until a newer event arrives. + Index *string `form:"index,omitempty" json:"index,omitempty"` + + // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + Wait *string `form:"wait,omitempty" json:"wait,omitempty"` +} + +// DeleteV0CityByCityNameConvoyByIdParams defines parameters for DeleteV0CityByCityNameConvoyById. +type DeleteV0CityByCityNameConvoyByIdParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameConvoyByIdAddParams defines parameters for PostV0CityByCityNameConvoyByIdAdd. +type PostV0CityByCityNameConvoyByIdAddParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameConvoyByIdCloseParams defines parameters for PostV0CityByCityNameConvoyByIdClose. +type PostV0CityByCityNameConvoyByIdCloseParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameConvoyByIdRemoveParams defines parameters for PostV0CityByCityNameConvoyByIdRemove. +type PostV0CityByCityNameConvoyByIdRemoveParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameConvoysParams defines parameters for GetV0CityByCityNameConvoys. +type GetV0CityByCityNameConvoysParams struct { + // Index Event sequence number; when provided, blocks until a newer event arrives. + Index *string `form:"index,omitempty" json:"index,omitempty"` + + // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + Wait *string `form:"wait,omitempty" json:"wait,omitempty"` + + // Cursor Pagination cursor from a previous response's next_cursor field. + Cursor *string `form:"cursor,omitempty" json:"cursor,omitempty"` + + // Limit Maximum number of results to return. 0 = server default. + Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +} + +// CreateConvoyParams defines parameters for CreateConvoy. +type CreateConvoyParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameEventsParams defines parameters for GetV0CityByCityNameEvents. +type GetV0CityByCityNameEventsParams struct { + // Index Event sequence number; when provided, blocks until a newer event arrives. + Index *string `form:"index,omitempty" json:"index,omitempty"` + + // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + Wait *string `form:"wait,omitempty" json:"wait,omitempty"` + + // Cursor Pagination cursor from a previous response's next_cursor field. + Cursor *string `form:"cursor,omitempty" json:"cursor,omitempty"` + + // Limit Maximum number of results to return. 0 = server default. + Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` + + // Type Filter by event type. + Type *string `form:"type,omitempty" json:"type,omitempty"` + + // Actor Filter by actor. + Actor *string `form:"actor,omitempty" json:"actor,omitempty"` + + // Since Filter events since duration ago (Go duration string, e.g. 5m). + Since *string `form:"since,omitempty" json:"since,omitempty"` +} + +// EmitEventParams defines parameters for EmitEvent. +type EmitEventParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// StreamEventsParams defines parameters for StreamEvents. +type StreamEventsParams struct { + // AfterSeq Reconnect position: only deliver events after this sequence number. + AfterSeq *string `form:"after_seq,omitempty" json:"after_seq,omitempty"` + + // LastEventID SSE reconnect position from the last received event ID. + LastEventID *string `json:"Last-Event-ID,omitempty"` +} + +// DeleteV0CityByCityNameExtmsgAdaptersParams defines parameters for DeleteV0CityByCityNameExtmsgAdapters. +type DeleteV0CityByCityNameExtmsgAdaptersParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// RegisterExtmsgAdapterParams defines parameters for RegisterExtmsgAdapter. +type RegisterExtmsgAdapterParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameExtmsgBindParams defines parameters for PostV0CityByCityNameExtmsgBind. +type PostV0CityByCityNameExtmsgBindParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameExtmsgBindingsParams defines parameters for GetV0CityByCityNameExtmsgBindings. +type GetV0CityByCityNameExtmsgBindingsParams struct { + // SessionId Session ID to list bindings for. + SessionId *string `form:"session_id,omitempty" json:"session_id,omitempty"` +} + +// GetV0CityByCityNameExtmsgGroupsParams defines parameters for GetV0CityByCityNameExtmsgGroups. +type GetV0CityByCityNameExtmsgGroupsParams struct { + // ScopeId Scope ID. + ScopeId *string `form:"scope_id,omitempty" json:"scope_id,omitempty"` + + // Provider Provider name. + Provider *string `form:"provider,omitempty" json:"provider,omitempty"` + + // AccountId Account ID. + AccountId *string `form:"account_id,omitempty" json:"account_id,omitempty"` + + // ConversationId Conversation ID. + ConversationId *string `form:"conversation_id,omitempty" json:"conversation_id,omitempty"` + + // Kind Conversation kind. + Kind *string `form:"kind,omitempty" json:"kind,omitempty"` +} + +// EnsureExtmsgGroupParams defines parameters for EnsureExtmsgGroup. +type EnsureExtmsgGroupParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameExtmsgInboundParams defines parameters for PostV0CityByCityNameExtmsgInbound. +type PostV0CityByCityNameExtmsgInboundParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameExtmsgOutboundParams defines parameters for PostV0CityByCityNameExtmsgOutbound. +type PostV0CityByCityNameExtmsgOutboundParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// DeleteV0CityByCityNameExtmsgParticipantsParams defines parameters for DeleteV0CityByCityNameExtmsgParticipants. +type DeleteV0CityByCityNameExtmsgParticipantsParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameExtmsgParticipantsParams defines parameters for PostV0CityByCityNameExtmsgParticipants. +type PostV0CityByCityNameExtmsgParticipantsParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameExtmsgTranscriptParams defines parameters for GetV0CityByCityNameExtmsgTranscript. +type GetV0CityByCityNameExtmsgTranscriptParams struct { + // ScopeId Scope ID. + ScopeId *string `form:"scope_id,omitempty" json:"scope_id,omitempty"` + + // Provider Provider name. + Provider *string `form:"provider,omitempty" json:"provider,omitempty"` + + // AccountId Account ID. + AccountId *string `form:"account_id,omitempty" json:"account_id,omitempty"` + + // ConversationId Conversation ID. + ConversationId *string `form:"conversation_id,omitempty" json:"conversation_id,omitempty"` + + // ParentConversationId Parent conversation ID. + ParentConversationId *string `form:"parent_conversation_id,omitempty" json:"parent_conversation_id,omitempty"` + + // Kind Conversation kind. + Kind *string `form:"kind,omitempty" json:"kind,omitempty"` +} + +// PostV0CityByCityNameExtmsgTranscriptAckParams defines parameters for PostV0CityByCityNameExtmsgTranscriptAck. +type PostV0CityByCityNameExtmsgTranscriptAckParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameExtmsgUnbindParams defines parameters for PostV0CityByCityNameExtmsgUnbind. +type PostV0CityByCityNameExtmsgUnbindParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameFormulaByNameParams defines parameters for GetV0CityByCityNameFormulaByName. +type GetV0CityByCityNameFormulaByNameParams struct { + // ScopeKind Scope kind (city or rig). + ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` + + // ScopeRef Scope reference. + ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` + + // Target Target agent for preview compilation. + Target string `form:"target" json:"target"` +} + +// GetV0CityByCityNameFormulasParams defines parameters for GetV0CityByCityNameFormulas. +type GetV0CityByCityNameFormulasParams struct { + // ScopeKind Scope kind (city or rig). + ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` + + // ScopeRef Scope reference. + ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` +} + +// GetV0CityByCityNameFormulasFeedParams defines parameters for GetV0CityByCityNameFormulasFeed. +type GetV0CityByCityNameFormulasFeedParams struct { + // ScopeKind Scope kind (city or rig). + ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` + + // ScopeRef Scope reference. + ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` + + // Limit Maximum number of feed items to return. 0 = default. + Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +} + +// GetV0CityByCityNameFormulasByNameParams defines parameters for GetV0CityByCityNameFormulasByName. +type GetV0CityByCityNameFormulasByNameParams struct { + // ScopeKind Scope kind (city or rig). + ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` + + // ScopeRef Scope reference. + ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` + + // Target Target agent for preview compilation. + Target string `form:"target" json:"target"` +} + +// PostV0CityByCityNameFormulasByNamePreviewParams defines parameters for PostV0CityByCityNameFormulasByNamePreview. +type PostV0CityByCityNameFormulasByNamePreviewParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameFormulasByNameRunsParams defines parameters for GetV0CityByCityNameFormulasByNameRuns. +type GetV0CityByCityNameFormulasByNameRunsParams struct { + // ScopeKind Scope kind (city or rig). + ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` + + // ScopeRef Scope reference. + ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` + + // Limit Maximum number of recent runs to return. 0 = default. + Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +} + +// GetV0CityByCityNameMailParams defines parameters for GetV0CityByCityNameMail. +type GetV0CityByCityNameMailParams struct { + // Index Event sequence number; when provided, blocks until a newer event arrives. + Index *string `form:"index,omitempty" json:"index,omitempty"` + + // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + Wait *string `form:"wait,omitempty" json:"wait,omitempty"` + + // Cursor Pagination cursor from a previous response's next_cursor field. + Cursor *string `form:"cursor,omitempty" json:"cursor,omitempty"` + + // Limit Maximum number of results to return. 0 = server default. + Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` + + // Agent Filter by agent name. + Agent *string `form:"agent,omitempty" json:"agent,omitempty"` + + // Status Filter by status (unread, all). + Status *string `form:"status,omitempty" json:"status,omitempty"` + + // Rig Filter by rig name. + Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +} + +// SendMailParams defines parameters for SendMail. +type SendMailParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` + + // IdempotencyKey Idempotency key for safe retries. + IdempotencyKey *string `json:"Idempotency-Key,omitempty"` +} + +// GetV0CityByCityNameMailCountParams defines parameters for GetV0CityByCityNameMailCount. +type GetV0CityByCityNameMailCountParams struct { + // Agent Filter by agent name. + Agent *string `form:"agent,omitempty" json:"agent,omitempty"` + + // Rig Filter by rig name. + Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +} + +// GetV0CityByCityNameMailThreadByIdParams defines parameters for GetV0CityByCityNameMailThreadById. +type GetV0CityByCityNameMailThreadByIdParams struct { + // Rig Filter by rig. + Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +} + +// DeleteV0CityByCityNameMailByIdParams defines parameters for DeleteV0CityByCityNameMailById. +type DeleteV0CityByCityNameMailByIdParams struct { + // Rig Rig hint. + Rig *string `form:"rig,omitempty" json:"rig,omitempty"` + + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameMailByIdParams defines parameters for GetV0CityByCityNameMailById. +type GetV0CityByCityNameMailByIdParams struct { + // Rig Rig hint for O(1) lookup. + Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +} + +// PostV0CityByCityNameMailByIdArchiveParams defines parameters for PostV0CityByCityNameMailByIdArchive. +type PostV0CityByCityNameMailByIdArchiveParams struct { + // Rig Rig hint. + Rig *string `form:"rig,omitempty" json:"rig,omitempty"` + + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameMailByIdMarkUnreadParams defines parameters for PostV0CityByCityNameMailByIdMarkUnread. +type PostV0CityByCityNameMailByIdMarkUnreadParams struct { + // Rig Rig hint. + Rig *string `form:"rig,omitempty" json:"rig,omitempty"` + + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameMailByIdReadParams defines parameters for PostV0CityByCityNameMailByIdRead. +type PostV0CityByCityNameMailByIdReadParams struct { + // Rig Rig hint. + Rig *string `form:"rig,omitempty" json:"rig,omitempty"` + + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// ReplyMailParams defines parameters for ReplyMail. +type ReplyMailParams struct { + // Rig Rig hint. + Rig *string `form:"rig,omitempty" json:"rig,omitempty"` + + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameOrderHistoryByBeadIdParams defines parameters for GetV0CityByCityNameOrderHistoryByBeadId. +type GetV0CityByCityNameOrderHistoryByBeadIdParams struct { + // StoreRef Store reference for disambiguating store-local bead IDs. + StoreRef *string `form:"store_ref,omitempty" json:"store_ref,omitempty"` +} + +// PostV0CityByCityNameOrderByNameDisableParams defines parameters for PostV0CityByCityNameOrderByNameDisable. +type PostV0CityByCityNameOrderByNameDisableParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameOrderByNameEnableParams defines parameters for PostV0CityByCityNameOrderByNameEnable. +type PostV0CityByCityNameOrderByNameEnableParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameOrdersFeedParams defines parameters for GetV0CityByCityNameOrdersFeed. +type GetV0CityByCityNameOrdersFeedParams struct { + // ScopeKind Scope kind (city or rig). + ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` + + // ScopeRef Scope reference. + ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` + + // Limit Maximum number of feed items to return. + Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +} + +// GetV0CityByCityNameOrdersHistoryParams defines parameters for GetV0CityByCityNameOrdersHistory. +type GetV0CityByCityNameOrdersHistoryParams struct { + // ScopedName Scoped order name. + ScopedName string `form:"scoped_name" json:"scoped_name"` + + // Limit Maximum number of history entries. 0 = default. + Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` + + // Before Return entries before this RFC3339 timestamp. + Before *string `form:"before,omitempty" json:"before,omitempty"` +} + +// DeleteV0CityByCityNamePatchesAgentByBaseParams defines parameters for DeleteV0CityByCityNamePatchesAgentByBase. +type DeleteV0CityByCityNamePatchesAgentByBaseParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// DeleteV0CityByCityNamePatchesAgentByDirByBaseParams defines parameters for DeleteV0CityByCityNamePatchesAgentByDirByBase. +type DeleteV0CityByCityNamePatchesAgentByDirByBaseParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PutV0CityByCityNamePatchesAgentsParams defines parameters for PutV0CityByCityNamePatchesAgents. +type PutV0CityByCityNamePatchesAgentsParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// DeleteV0CityByCityNamePatchesProviderByNameParams defines parameters for DeleteV0CityByCityNamePatchesProviderByName. +type DeleteV0CityByCityNamePatchesProviderByNameParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PutV0CityByCityNamePatchesProvidersParams defines parameters for PutV0CityByCityNamePatchesProviders. +type PutV0CityByCityNamePatchesProvidersParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// DeleteV0CityByCityNamePatchesRigByNameParams defines parameters for DeleteV0CityByCityNamePatchesRigByName. +type DeleteV0CityByCityNamePatchesRigByNameParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PutV0CityByCityNamePatchesRigsParams defines parameters for PutV0CityByCityNamePatchesRigs. +type PutV0CityByCityNamePatchesRigsParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameProviderReadinessParams defines parameters for GetV0CityByCityNameProviderReadiness. +type GetV0CityByCityNameProviderReadinessParams struct { + // Providers Comma-separated provider names to check (default: claude,codex,gemini). + Providers *string `form:"providers,omitempty" json:"providers,omitempty"` + + // Fresh Force fresh probe, bypassing cache. + Fresh *bool `form:"fresh,omitempty" json:"fresh,omitempty"` +} + +// DeleteV0CityByCityNameProviderByNameParams defines parameters for DeleteV0CityByCityNameProviderByName. +type DeleteV0CityByCityNameProviderByNameParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PatchV0CityByCityNameProviderByNameParams defines parameters for PatchV0CityByCityNameProviderByName. +type PatchV0CityByCityNameProviderByNameParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// CreateProviderParams defines parameters for CreateProvider. +type CreateProviderParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameReadinessParams defines parameters for GetV0CityByCityNameReadiness. +type GetV0CityByCityNameReadinessParams struct { + // Items Comma-separated readiness items to check (default: claude,codex,gemini,github_cli). + Items *string `form:"items,omitempty" json:"items,omitempty"` + + // Fresh Force fresh probe, bypassing cache. + Fresh *bool `form:"fresh,omitempty" json:"fresh,omitempty"` +} + +// DeleteV0CityByCityNameRigByNameParams defines parameters for DeleteV0CityByCityNameRigByName. +type DeleteV0CityByCityNameRigByNameParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameRigByNameParams defines parameters for GetV0CityByCityNameRigByName. +type GetV0CityByCityNameRigByNameParams struct { + // Git Include git status. + Git *bool `form:"git,omitempty" json:"git,omitempty"` +} + +// PatchV0CityByCityNameRigByNameParams defines parameters for PatchV0CityByCityNameRigByName. +type PatchV0CityByCityNameRigByNameParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameRigByNameByActionParams defines parameters for PostV0CityByCityNameRigByNameByAction. +type PostV0CityByCityNameRigByNameByActionParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameRigsParams defines parameters for GetV0CityByCityNameRigs. +type GetV0CityByCityNameRigsParams struct { + // Index Event sequence number; when provided, blocks until a newer event arrives. + Index *string `form:"index,omitempty" json:"index,omitempty"` + + // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + Wait *string `form:"wait,omitempty" json:"wait,omitempty"` + + // Git Include git status. + Git *bool `form:"git,omitempty" json:"git,omitempty"` +} + +// CreateRigParams defines parameters for CreateRig. +type CreateRigParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameServiceByNameRestartParams defines parameters for PostV0CityByCityNameServiceByNameRestart. +type PostV0CityByCityNameServiceByNameRestartParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameSessionByIdParams defines parameters for GetV0CityByCityNameSessionById. +type GetV0CityByCityNameSessionByIdParams struct { + // Peek Include last output preview. + Peek *bool `form:"peek,omitempty" json:"peek,omitempty"` +} + +// PatchV0CityByCityNameSessionByIdParams defines parameters for PatchV0CityByCityNameSessionById. +type PatchV0CityByCityNameSessionByIdParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameSessionByIdCloseParams defines parameters for PostV0CityByCityNameSessionByIdClose. +type PostV0CityByCityNameSessionByIdCloseParams struct { + // Delete Permanently delete bead after closing. + Delete *bool `form:"delete,omitempty" json:"delete,omitempty"` + + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameSessionByIdKillParams defines parameters for PostV0CityByCityNameSessionByIdKill. +type PostV0CityByCityNameSessionByIdKillParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// SendSessionMessageParams defines parameters for SendSessionMessage. +type SendSessionMessageParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameSessionByIdRenameParams defines parameters for PostV0CityByCityNameSessionByIdRename. +type PostV0CityByCityNameSessionByIdRenameParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// RespondSessionParams defines parameters for RespondSession. +type RespondSessionParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameSessionByIdStopParams defines parameters for PostV0CityByCityNameSessionByIdStop. +type PostV0CityByCityNameSessionByIdStopParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// StreamSessionParams defines parameters for StreamSession. +type StreamSessionParams struct { + // Format Transcript format: conversation (default) or raw. + Format *string `form:"format,omitempty" json:"format,omitempty"` +} + +// SubmitSessionParams defines parameters for SubmitSession. +type SubmitSessionParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameSessionByIdSuspendParams defines parameters for PostV0CityByCityNameSessionByIdSuspend. +type PostV0CityByCityNameSessionByIdSuspendParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameSessionByIdTranscriptParams defines parameters for GetV0CityByCityNameSessionByIdTranscript. +type GetV0CityByCityNameSessionByIdTranscriptParams struct { + // Tail Number of recent compaction segments to return. This API parameter keeps compaction-segment semantics even though gc session logs --tail counts displayed transcript entries. Omit for the endpoint default (usually 1); 0 returns all segments; N>0 returns the last N. + Tail *string `form:"tail,omitempty" json:"tail,omitempty"` + + // Format Transcript format: conversation (default) or raw. + Format *string `form:"format,omitempty" json:"format,omitempty"` + + // Before Pagination cursor: return entries before this UUID. + Before *string `form:"before,omitempty" json:"before,omitempty"` +} + +// PostV0CityByCityNameSessionByIdWakeParams defines parameters for PostV0CityByCityNameSessionByIdWake. +type PostV0CityByCityNameSessionByIdWakeParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameSessionsParams defines parameters for GetV0CityByCityNameSessions. +type GetV0CityByCityNameSessionsParams struct { + // Cursor Pagination cursor from a previous response's next_cursor field. + Cursor *string `form:"cursor,omitempty" json:"cursor,omitempty"` + + // Limit Maximum number of results to return. 0 = server default. + Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` + + // State Filter by session state (e.g. active, closed). + State *string `form:"state,omitempty" json:"state,omitempty"` + + // Template Filter by session template (agent qualified name). + Template *string `form:"template,omitempty" json:"template,omitempty"` + + // Peek Include last output preview. + Peek *bool `form:"peek,omitempty" json:"peek,omitempty"` +} + +// CreateSessionParams defines parameters for CreateSession. +type CreateSessionParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// PostV0CityByCityNameSlingParams defines parameters for PostV0CityByCityNameSling. +type PostV0CityByCityNameSlingParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameStatusParams defines parameters for GetV0CityByCityNameStatus. +type GetV0CityByCityNameStatusParams struct { + // Index Event sequence number; when provided, blocks until a newer event arrives. + Index *string `form:"index,omitempty" json:"index,omitempty"` + + // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. + Wait *string `form:"wait,omitempty" json:"wait,omitempty"` +} + +// PostV0CityByCityNameUnregisterParams defines parameters for PostV0CityByCityNameUnregister. +type PostV0CityByCityNameUnregisterParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// DeleteV0CityByCityNameWorkflowByWorkflowIdParams defines parameters for DeleteV0CityByCityNameWorkflowByWorkflowId. +type DeleteV0CityByCityNameWorkflowByWorkflowIdParams struct { + // ScopeKind Scope kind (city or rig). + ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` + + // ScopeRef Scope reference. + ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` + + // Delete Permanently delete beads from store. + Delete *bool `form:"delete,omitempty" json:"delete,omitempty"` + + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + +// GetV0CityByCityNameWorkflowByWorkflowIdParams defines parameters for GetV0CityByCityNameWorkflowByWorkflowId. +type GetV0CityByCityNameWorkflowByWorkflowIdParams struct { + // ScopeKind Scope kind (city or rig). + ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` + + // ScopeRef Scope reference. + ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` +} + +// GetV0EventsParams defines parameters for GetV0Events. +type GetV0EventsParams struct { + // Type Filter by event type. + Type *string `form:"type,omitempty" json:"type,omitempty"` + + // Actor Filter by actor. + Actor *string `form:"actor,omitempty" json:"actor,omitempty"` + + // Since Filter to events within the last Go duration (e.g. "5m"). + Since *string `form:"since,omitempty" json:"since,omitempty"` + + // Limit Maximum number of trailing events to return. 0 = no limit. Used by 'gc events --seq' to compute the head cursor cheaply. + Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +} + +// StreamSupervisorEventsParams defines parameters for StreamSupervisorEvents. +type StreamSupervisorEventsParams struct { + // AfterCursor Alternative to Last-Event-ID for browsers that can't set custom headers. + AfterCursor *string `form:"after_cursor,omitempty" json:"after_cursor,omitempty"` + + // LastEventID Reconnect cursor (composite per-city cursor). + LastEventID *string `json:"Last-Event-ID,omitempty"` +} + +// GetV0ProviderReadinessParams defines parameters for GetV0ProviderReadiness. +type GetV0ProviderReadinessParams struct { + // Providers Comma-separated list of providers to probe. + Providers *string `form:"providers,omitempty" json:"providers,omitempty"` + + // Fresh Force fresh probe, bypassing cache. + Fresh *bool `form:"fresh,omitempty" json:"fresh,omitempty"` +} + +// GetV0ReadinessParams defines parameters for GetV0Readiness. +type GetV0ReadinessParams struct { + // Items Comma-separated list of readiness items to check. + Items *string `form:"items,omitempty" json:"items,omitempty"` + + // Fresh Force fresh probe, bypassing cache. + Fresh *bool `form:"fresh,omitempty" json:"fresh,omitempty"` +} + +// PostV0CityJSONRequestBody defines body for PostV0City for application/json ContentType. +type PostV0CityJSONRequestBody = CityCreateRequest + +// PatchV0CityByCityNameJSONRequestBody defines body for PatchV0CityByCityName for application/json ContentType. +type PatchV0CityByCityNameJSONRequestBody = CityPatchInputBody + +// PatchV0CityByCityNameAgentByBaseJSONRequestBody defines body for PatchV0CityByCityNameAgentByBase for application/json ContentType. +type PatchV0CityByCityNameAgentByBaseJSONRequestBody = AgentUpdateInputBody + +// PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody defines body for PatchV0CityByCityNameAgentByDirByBase for application/json ContentType. +type PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody = AgentUpdateQualifiedInputBody + +// CreateAgentJSONRequestBody defines body for CreateAgent for application/json ContentType. +type CreateAgentJSONRequestBody = AgentCreateInputBody + +// PatchV0CityByCityNameBeadByIdJSONRequestBody defines body for PatchV0CityByCityNameBeadById for application/json ContentType. +type PatchV0CityByCityNameBeadByIdJSONRequestBody = BeadUpdateBody + +// PostV0CityByCityNameBeadByIdAssignJSONRequestBody defines body for PostV0CityByCityNameBeadByIdAssign for application/json ContentType. +type PostV0CityByCityNameBeadByIdAssignJSONRequestBody = BeadAssignInputBody + +// PostV0CityByCityNameBeadByIdUpdateJSONRequestBody defines body for PostV0CityByCityNameBeadByIdUpdate for application/json ContentType. +type PostV0CityByCityNameBeadByIdUpdateJSONRequestBody = BeadUpdateBody + +// CreateBeadJSONRequestBody defines body for CreateBead for application/json ContentType. +type CreateBeadJSONRequestBody = BeadCreateInputBody + +// PostV0CityByCityNameConvoyByIdAddJSONRequestBody defines body for PostV0CityByCityNameConvoyByIdAdd for application/json ContentType. +type PostV0CityByCityNameConvoyByIdAddJSONRequestBody = ConvoyAddInputBody + +// PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody defines body for PostV0CityByCityNameConvoyByIdRemove for application/json ContentType. +type PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody = ConvoyRemoveInputBody + +// CreateConvoyJSONRequestBody defines body for CreateConvoy for application/json ContentType. +type CreateConvoyJSONRequestBody = ConvoyCreateInputBody + +// EmitEventJSONRequestBody defines body for EmitEvent for application/json ContentType. +type EmitEventJSONRequestBody = EventEmitRequest + +// DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody defines body for DeleteV0CityByCityNameExtmsgAdapters for application/json ContentType. +type DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody = ExtMsgAdapterUnregisterInputBody + +// RegisterExtmsgAdapterJSONRequestBody defines body for RegisterExtmsgAdapter for application/json ContentType. +type RegisterExtmsgAdapterJSONRequestBody = ExtMsgAdapterRegisterInputBody + +// PostV0CityByCityNameExtmsgBindJSONRequestBody defines body for PostV0CityByCityNameExtmsgBind for application/json ContentType. +type PostV0CityByCityNameExtmsgBindJSONRequestBody = ExtMsgBindInputBody + +// EnsureExtmsgGroupJSONRequestBody defines body for EnsureExtmsgGroup for application/json ContentType. +type EnsureExtmsgGroupJSONRequestBody = ExtMsgGroupEnsureInputBody + +// PostV0CityByCityNameExtmsgInboundJSONRequestBody defines body for PostV0CityByCityNameExtmsgInbound for application/json ContentType. +type PostV0CityByCityNameExtmsgInboundJSONRequestBody = ExtMsgInboundInputBody + +// PostV0CityByCityNameExtmsgOutboundJSONRequestBody defines body for PostV0CityByCityNameExtmsgOutbound for application/json ContentType. +type PostV0CityByCityNameExtmsgOutboundJSONRequestBody = ExtMsgOutboundInputBody + +// DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody defines body for DeleteV0CityByCityNameExtmsgParticipants for application/json ContentType. +type DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody = ExtMsgParticipantRemoveInputBody + +// PostV0CityByCityNameExtmsgParticipantsJSONRequestBody defines body for PostV0CityByCityNameExtmsgParticipants for application/json ContentType. +type PostV0CityByCityNameExtmsgParticipantsJSONRequestBody = ExtMsgParticipantUpsertInputBody + +// PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody defines body for PostV0CityByCityNameExtmsgTranscriptAck for application/json ContentType. +type PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody = ExtMsgTranscriptAckInputBody + +// PostV0CityByCityNameExtmsgUnbindJSONRequestBody defines body for PostV0CityByCityNameExtmsgUnbind for application/json ContentType. +type PostV0CityByCityNameExtmsgUnbindJSONRequestBody = ExtMsgUnbindInputBody + +// PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody defines body for PostV0CityByCityNameFormulasByNamePreview for application/json ContentType. +type PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody = FormulaPreviewBody + +// SendMailJSONRequestBody defines body for SendMail for application/json ContentType. +type SendMailJSONRequestBody = MailSendInputBody + +// ReplyMailJSONRequestBody defines body for ReplyMail for application/json ContentType. +type ReplyMailJSONRequestBody = MailReplyInputBody + +// PutV0CityByCityNamePatchesAgentsJSONRequestBody defines body for PutV0CityByCityNamePatchesAgents for application/json ContentType. +type PutV0CityByCityNamePatchesAgentsJSONRequestBody = AgentPatchSetInputBody + +// PutV0CityByCityNamePatchesProvidersJSONRequestBody defines body for PutV0CityByCityNamePatchesProviders for application/json ContentType. +type PutV0CityByCityNamePatchesProvidersJSONRequestBody = ProviderPatchSetInputBody + +// PutV0CityByCityNamePatchesRigsJSONRequestBody defines body for PutV0CityByCityNamePatchesRigs for application/json ContentType. +type PutV0CityByCityNamePatchesRigsJSONRequestBody = RigPatchSetInputBody + +// PatchV0CityByCityNameProviderByNameJSONRequestBody defines body for PatchV0CityByCityNameProviderByName for application/json ContentType. +type PatchV0CityByCityNameProviderByNameJSONRequestBody = ProviderUpdateInputBody + +// CreateProviderJSONRequestBody defines body for CreateProvider for application/json ContentType. +type CreateProviderJSONRequestBody = ProviderCreateInputBody + +// PatchV0CityByCityNameRigByNameJSONRequestBody defines body for PatchV0CityByCityNameRigByName for application/json ContentType. +type PatchV0CityByCityNameRigByNameJSONRequestBody = RigUpdateInputBody + +// CreateRigJSONRequestBody defines body for CreateRig for application/json ContentType. +type CreateRigJSONRequestBody = RigCreateInputBody + +// PatchV0CityByCityNameSessionByIdJSONRequestBody defines body for PatchV0CityByCityNameSessionById for application/json ContentType. +type PatchV0CityByCityNameSessionByIdJSONRequestBody = SessionPatchBody + +// SendSessionMessageJSONRequestBody defines body for SendSessionMessage for application/json ContentType. +type SendSessionMessageJSONRequestBody = SessionMessageInputBody + +// PostV0CityByCityNameSessionByIdRenameJSONRequestBody defines body for PostV0CityByCityNameSessionByIdRename for application/json ContentType. +type PostV0CityByCityNameSessionByIdRenameJSONRequestBody = SessionRenameInputBody + +// RespondSessionJSONRequestBody defines body for RespondSession for application/json ContentType. +type RespondSessionJSONRequestBody = SessionRespondInputBody + +// SubmitSessionJSONRequestBody defines body for SubmitSession for application/json ContentType. +type SubmitSessionJSONRequestBody = SessionSubmitInputBody + +// CreateSessionJSONRequestBody defines body for CreateSession for application/json ContentType. +type CreateSessionJSONRequestBody = SessionCreateBody + +// PostV0CityByCityNameSlingJSONRequestBody defines body for PostV0CityByCityNameSling for application/json ContentType. +type PostV0CityByCityNameSlingJSONRequestBody = SlingInputBody + +// AsAdapterEventPayload returns the union data inside the EventPayload as a AdapterEventPayload +func (t EventPayload) AsAdapterEventPayload() (AdapterEventPayload, error) { + var body AdapterEventPayload + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromAdapterEventPayload overwrites any union data inside the EventPayload as the provided AdapterEventPayload +func (t *EventPayload) FromAdapterEventPayload(v AdapterEventPayload) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeAdapterEventPayload performs a merge with any union data inside the EventPayload, using the provided AdapterEventPayload +func (t *EventPayload) MergeAdapterEventPayload(v AdapterEventPayload) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsBeadEventPayload returns the union data inside the EventPayload as a BeadEventPayload +func (t EventPayload) AsBeadEventPayload() (BeadEventPayload, error) { + var body BeadEventPayload + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromBeadEventPayload overwrites any union data inside the EventPayload as the provided BeadEventPayload +func (t *EventPayload) FromBeadEventPayload(v BeadEventPayload) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeBeadEventPayload performs a merge with any union data inside the EventPayload, using the provided BeadEventPayload +func (t *EventPayload) MergeBeadEventPayload(v BeadEventPayload) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsBoundEventPayload returns the union data inside the EventPayload as a BoundEventPayload +func (t EventPayload) AsBoundEventPayload() (BoundEventPayload, error) { + var body BoundEventPayload + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromBoundEventPayload overwrites any union data inside the EventPayload as the provided BoundEventPayload +func (t *EventPayload) FromBoundEventPayload(v BoundEventPayload) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeBoundEventPayload performs a merge with any union data inside the EventPayload, using the provided BoundEventPayload +func (t *EventPayload) MergeBoundEventPayload(v BoundEventPayload) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsCityLifecyclePayload returns the union data inside the EventPayload as a CityLifecyclePayload +func (t EventPayload) AsCityLifecyclePayload() (CityLifecyclePayload, error) { + var body CityLifecyclePayload + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromCityLifecyclePayload overwrites any union data inside the EventPayload as the provided CityLifecyclePayload +func (t *EventPayload) FromCityLifecyclePayload(v CityLifecyclePayload) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeCityLifecyclePayload performs a merge with any union data inside the EventPayload, using the provided CityLifecyclePayload +func (t *EventPayload) MergeCityLifecyclePayload(v CityLifecyclePayload) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsGroupCreatedEventPayload returns the union data inside the EventPayload as a GroupCreatedEventPayload +func (t EventPayload) AsGroupCreatedEventPayload() (GroupCreatedEventPayload, error) { + var body GroupCreatedEventPayload + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromGroupCreatedEventPayload overwrites any union data inside the EventPayload as the provided GroupCreatedEventPayload +func (t *EventPayload) FromGroupCreatedEventPayload(v GroupCreatedEventPayload) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeGroupCreatedEventPayload performs a merge with any union data inside the EventPayload, using the provided GroupCreatedEventPayload +func (t *EventPayload) MergeGroupCreatedEventPayload(v GroupCreatedEventPayload) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsInboundEventPayload returns the union data inside the EventPayload as a InboundEventPayload +func (t EventPayload) AsInboundEventPayload() (InboundEventPayload, error) { + var body InboundEventPayload + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromInboundEventPayload overwrites any union data inside the EventPayload as the provided InboundEventPayload +func (t *EventPayload) FromInboundEventPayload(v InboundEventPayload) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeInboundEventPayload performs a merge with any union data inside the EventPayload, using the provided InboundEventPayload +func (t *EventPayload) MergeInboundEventPayload(v InboundEventPayload) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsMailEventPayload returns the union data inside the EventPayload as a MailEventPayload +func (t EventPayload) AsMailEventPayload() (MailEventPayload, error) { + var body MailEventPayload + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromMailEventPayload overwrites any union data inside the EventPayload as the provided MailEventPayload +func (t *EventPayload) FromMailEventPayload(v MailEventPayload) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeMailEventPayload performs a merge with any union data inside the EventPayload, using the provided MailEventPayload +func (t *EventPayload) MergeMailEventPayload(v MailEventPayload) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsNoPayload returns the union data inside the EventPayload as a NoPayload +func (t EventPayload) AsNoPayload() (NoPayload, error) { + var body NoPayload + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNoPayload overwrites any union data inside the EventPayload as the provided NoPayload +func (t *EventPayload) FromNoPayload(v NoPayload) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNoPayload performs a merge with any union data inside the EventPayload, using the provided NoPayload +func (t *EventPayload) MergeNoPayload(v NoPayload) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsOutboundEventPayload returns the union data inside the EventPayload as a OutboundEventPayload +func (t EventPayload) AsOutboundEventPayload() (OutboundEventPayload, error) { + var body OutboundEventPayload + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromOutboundEventPayload overwrites any union data inside the EventPayload as the provided OutboundEventPayload +func (t *EventPayload) FromOutboundEventPayload(v OutboundEventPayload) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeOutboundEventPayload performs a merge with any union data inside the EventPayload, using the provided OutboundEventPayload +func (t *EventPayload) MergeOutboundEventPayload(v OutboundEventPayload) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsUnboundEventPayload returns the union data inside the EventPayload as a UnboundEventPayload +func (t EventPayload) AsUnboundEventPayload() (UnboundEventPayload, error) { + var body UnboundEventPayload + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromUnboundEventPayload overwrites any union data inside the EventPayload as the provided UnboundEventPayload +func (t *EventPayload) FromUnboundEventPayload(v UnboundEventPayload) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeUnboundEventPayload performs a merge with any union data inside the EventPayload, using the provided UnboundEventPayload +func (t *EventPayload) MergeUnboundEventPayload(v UnboundEventPayload) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsWorkerOperationEventPayload returns the union data inside the EventPayload as a WorkerOperationEventPayload +func (t EventPayload) AsWorkerOperationEventPayload() (WorkerOperationEventPayload, error) { + var body WorkerOperationEventPayload + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromWorkerOperationEventPayload overwrites any union data inside the EventPayload as the provided WorkerOperationEventPayload +func (t *EventPayload) FromWorkerOperationEventPayload(v WorkerOperationEventPayload) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeWorkerOperationEventPayload performs a merge with any union data inside the EventPayload, using the provided WorkerOperationEventPayload +func (t *EventPayload) MergeWorkerOperationEventPayload(v WorkerOperationEventPayload) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t EventPayload) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *EventPayload) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsSessionActivityEvent returns the union data inside the SessionStreamCommonEvent as a SessionActivityEvent +func (t SessionStreamCommonEvent) AsSessionActivityEvent() (SessionActivityEvent, error) { + var body SessionActivityEvent + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromSessionActivityEvent overwrites any union data inside the SessionStreamCommonEvent as the provided SessionActivityEvent +func (t *SessionStreamCommonEvent) FromSessionActivityEvent(v SessionActivityEvent) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeSessionActivityEvent performs a merge with any union data inside the SessionStreamCommonEvent, using the provided SessionActivityEvent +func (t *SessionStreamCommonEvent) MergeSessionActivityEvent(v SessionActivityEvent) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsPendingInteraction returns the union data inside the SessionStreamCommonEvent as a PendingInteraction +func (t SessionStreamCommonEvent) AsPendingInteraction() (PendingInteraction, error) { + var body PendingInteraction + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromPendingInteraction overwrites any union data inside the SessionStreamCommonEvent as the provided PendingInteraction +func (t *SessionStreamCommonEvent) FromPendingInteraction(v PendingInteraction) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergePendingInteraction performs a merge with any union data inside the SessionStreamCommonEvent, using the provided PendingInteraction +func (t *SessionStreamCommonEvent) MergePendingInteraction(v PendingInteraction) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsHeartbeatEvent returns the union data inside the SessionStreamCommonEvent as a HeartbeatEvent +func (t SessionStreamCommonEvent) AsHeartbeatEvent() (HeartbeatEvent, error) { + var body HeartbeatEvent + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromHeartbeatEvent overwrites any union data inside the SessionStreamCommonEvent as the provided HeartbeatEvent +func (t *SessionStreamCommonEvent) FromHeartbeatEvent(v HeartbeatEvent) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeHeartbeatEvent performs a merge with any union data inside the SessionStreamCommonEvent, using the provided HeartbeatEvent +func (t *SessionStreamCommonEvent) MergeHeartbeatEvent(v HeartbeatEvent) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t SessionStreamCommonEvent) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *SessionStreamCommonEvent) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsTypedEventStreamEnvelopeBeadClosed returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeBeadClosed +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeBeadClosed() (TypedEventStreamEnvelopeBeadClosed, error) { + var body TypedEventStreamEnvelopeBeadClosed + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeBeadClosed overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeBeadClosed +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeBeadClosed(v TypedEventStreamEnvelopeBeadClosed) error { + v.Type = "bead.closed" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeBeadClosed performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeBeadClosed +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeBeadClosed(v TypedEventStreamEnvelopeBeadClosed) error { + v.Type = "bead.closed" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeBeadCreated returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeBeadCreated +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeBeadCreated() (TypedEventStreamEnvelopeBeadCreated, error) { + var body TypedEventStreamEnvelopeBeadCreated + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeBeadCreated overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeBeadCreated +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeBeadCreated(v TypedEventStreamEnvelopeBeadCreated) error { + v.Type = "bead.created" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeBeadCreated performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeBeadCreated +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeBeadCreated(v TypedEventStreamEnvelopeBeadCreated) error { + v.Type = "bead.created" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeBeadUpdated returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeBeadUpdated +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeBeadUpdated() (TypedEventStreamEnvelopeBeadUpdated, error) { + var body TypedEventStreamEnvelopeBeadUpdated + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeBeadUpdated overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeBeadUpdated +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeBeadUpdated(v TypedEventStreamEnvelopeBeadUpdated) error { + v.Type = "bead.updated" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeBeadUpdated performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeBeadUpdated +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeBeadUpdated(v TypedEventStreamEnvelopeBeadUpdated) error { + v.Type = "bead.updated" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeCityCreated returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeCityCreated +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeCityCreated() (TypedEventStreamEnvelopeCityCreated, error) { + var body TypedEventStreamEnvelopeCityCreated + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeCityCreated overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeCityCreated +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeCityCreated(v TypedEventStreamEnvelopeCityCreated) error { + v.Type = "city.created" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeCityCreated performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeCityCreated +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeCityCreated(v TypedEventStreamEnvelopeCityCreated) error { + v.Type = "city.created" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeCityInitFailed returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeCityInitFailed +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeCityInitFailed() (TypedEventStreamEnvelopeCityInitFailed, error) { + var body TypedEventStreamEnvelopeCityInitFailed + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeCityInitFailed overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeCityInitFailed +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeCityInitFailed(v TypedEventStreamEnvelopeCityInitFailed) error { + v.Type = "city.init_failed" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeCityInitFailed performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeCityInitFailed +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeCityInitFailed(v TypedEventStreamEnvelopeCityInitFailed) error { + v.Type = "city.init_failed" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeCityReady returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeCityReady +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeCityReady() (TypedEventStreamEnvelopeCityReady, error) { + var body TypedEventStreamEnvelopeCityReady + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeCityReady overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeCityReady +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeCityReady(v TypedEventStreamEnvelopeCityReady) error { + v.Type = "city.ready" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeCityReady performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeCityReady +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeCityReady(v TypedEventStreamEnvelopeCityReady) error { + v.Type = "city.ready" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeCityResumed returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeCityResumed +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeCityResumed() (TypedEventStreamEnvelopeCityResumed, error) { + var body TypedEventStreamEnvelopeCityResumed + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeCityResumed overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeCityResumed +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeCityResumed(v TypedEventStreamEnvelopeCityResumed) error { + v.Type = "city.resumed" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeCityResumed performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeCityResumed +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeCityResumed(v TypedEventStreamEnvelopeCityResumed) error { + v.Type = "city.resumed" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeCitySuspended returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeCitySuspended +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeCitySuspended() (TypedEventStreamEnvelopeCitySuspended, error) { + var body TypedEventStreamEnvelopeCitySuspended + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeCitySuspended overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeCitySuspended +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeCitySuspended(v TypedEventStreamEnvelopeCitySuspended) error { + v.Type = "city.suspended" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeCitySuspended performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeCitySuspended +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeCitySuspended(v TypedEventStreamEnvelopeCitySuspended) error { + v.Type = "city.suspended" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeCityUnregisterFailed returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeCityUnregisterFailed +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeCityUnregisterFailed() (TypedEventStreamEnvelopeCityUnregisterFailed, error) { + var body TypedEventStreamEnvelopeCityUnregisterFailed + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeCityUnregisterFailed overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeCityUnregisterFailed +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeCityUnregisterFailed(v TypedEventStreamEnvelopeCityUnregisterFailed) error { + v.Type = "city.unregister_failed" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeCityUnregisterFailed performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeCityUnregisterFailed +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeCityUnregisterFailed(v TypedEventStreamEnvelopeCityUnregisterFailed) error { + v.Type = "city.unregister_failed" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeCityUnregisterRequested returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeCityUnregisterRequested +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeCityUnregisterRequested() (TypedEventStreamEnvelopeCityUnregisterRequested, error) { + var body TypedEventStreamEnvelopeCityUnregisterRequested + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeCityUnregisterRequested overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeCityUnregisterRequested +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeCityUnregisterRequested(v TypedEventStreamEnvelopeCityUnregisterRequested) error { + v.Type = "city.unregister_requested" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeCityUnregisterRequested performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeCityUnregisterRequested +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeCityUnregisterRequested(v TypedEventStreamEnvelopeCityUnregisterRequested) error { + v.Type = "city.unregister_requested" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeCityUnregistered returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeCityUnregistered +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeCityUnregistered() (TypedEventStreamEnvelopeCityUnregistered, error) { + var body TypedEventStreamEnvelopeCityUnregistered + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeCityUnregistered overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeCityUnregistered +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeCityUnregistered(v TypedEventStreamEnvelopeCityUnregistered) error { + v.Type = "city.unregistered" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeCityUnregistered performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeCityUnregistered +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeCityUnregistered(v TypedEventStreamEnvelopeCityUnregistered) error { + v.Type = "city.unregistered" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeControllerStarted returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeControllerStarted +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeControllerStarted() (TypedEventStreamEnvelopeControllerStarted, error) { + var body TypedEventStreamEnvelopeControllerStarted + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeControllerStarted overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeControllerStarted +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeControllerStarted(v TypedEventStreamEnvelopeControllerStarted) error { + v.Type = "controller.started" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeControllerStarted performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeControllerStarted +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeControllerStarted(v TypedEventStreamEnvelopeControllerStarted) error { + v.Type = "controller.started" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeControllerStopped returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeControllerStopped +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeControllerStopped() (TypedEventStreamEnvelopeControllerStopped, error) { + var body TypedEventStreamEnvelopeControllerStopped + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeControllerStopped overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeControllerStopped +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeControllerStopped(v TypedEventStreamEnvelopeControllerStopped) error { + v.Type = "controller.stopped" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeControllerStopped performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeControllerStopped +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeControllerStopped(v TypedEventStreamEnvelopeControllerStopped) error { + v.Type = "controller.stopped" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeConvoyClosed returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeConvoyClosed +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeConvoyClosed() (TypedEventStreamEnvelopeConvoyClosed, error) { + var body TypedEventStreamEnvelopeConvoyClosed + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeConvoyClosed overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeConvoyClosed +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeConvoyClosed(v TypedEventStreamEnvelopeConvoyClosed) error { + v.Type = "convoy.closed" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeConvoyClosed performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeConvoyClosed +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeConvoyClosed(v TypedEventStreamEnvelopeConvoyClosed) error { + v.Type = "convoy.closed" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeConvoyCreated returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeConvoyCreated +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeConvoyCreated() (TypedEventStreamEnvelopeConvoyCreated, error) { + var body TypedEventStreamEnvelopeConvoyCreated + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeConvoyCreated overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeConvoyCreated +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeConvoyCreated(v TypedEventStreamEnvelopeConvoyCreated) error { + v.Type = "convoy.created" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeConvoyCreated performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeConvoyCreated +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeConvoyCreated(v TypedEventStreamEnvelopeConvoyCreated) error { + v.Type = "convoy.created" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeExtmsgAdapterAdded returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeExtmsgAdapterAdded +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeExtmsgAdapterAdded() (TypedEventStreamEnvelopeExtmsgAdapterAdded, error) { + var body TypedEventStreamEnvelopeExtmsgAdapterAdded + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeExtmsgAdapterAdded overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeExtmsgAdapterAdded +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeExtmsgAdapterAdded(v TypedEventStreamEnvelopeExtmsgAdapterAdded) error { + v.Type = "extmsg.adapter_added" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeExtmsgAdapterAdded performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeExtmsgAdapterAdded +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeExtmsgAdapterAdded(v TypedEventStreamEnvelopeExtmsgAdapterAdded) error { + v.Type = "extmsg.adapter_added" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeExtmsgAdapterRemoved returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeExtmsgAdapterRemoved +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeExtmsgAdapterRemoved() (TypedEventStreamEnvelopeExtmsgAdapterRemoved, error) { + var body TypedEventStreamEnvelopeExtmsgAdapterRemoved + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeExtmsgAdapterRemoved overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeExtmsgAdapterRemoved +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeExtmsgAdapterRemoved(v TypedEventStreamEnvelopeExtmsgAdapterRemoved) error { + v.Type = "extmsg.adapter_removed" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeExtmsgAdapterRemoved performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeExtmsgAdapterRemoved +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeExtmsgAdapterRemoved(v TypedEventStreamEnvelopeExtmsgAdapterRemoved) error { + v.Type = "extmsg.adapter_removed" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeExtmsgBound returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeExtmsgBound +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeExtmsgBound() (TypedEventStreamEnvelopeExtmsgBound, error) { + var body TypedEventStreamEnvelopeExtmsgBound + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeExtmsgBound overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeExtmsgBound +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeExtmsgBound(v TypedEventStreamEnvelopeExtmsgBound) error { + v.Type = "extmsg.bound" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeExtmsgBound performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeExtmsgBound +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeExtmsgBound(v TypedEventStreamEnvelopeExtmsgBound) error { + v.Type = "extmsg.bound" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeExtmsgGroupCreated returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeExtmsgGroupCreated +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeExtmsgGroupCreated() (TypedEventStreamEnvelopeExtmsgGroupCreated, error) { + var body TypedEventStreamEnvelopeExtmsgGroupCreated + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeExtmsgGroupCreated overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeExtmsgGroupCreated +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeExtmsgGroupCreated(v TypedEventStreamEnvelopeExtmsgGroupCreated) error { + v.Type = "extmsg.group_created" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeExtmsgGroupCreated performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeExtmsgGroupCreated +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeExtmsgGroupCreated(v TypedEventStreamEnvelopeExtmsgGroupCreated) error { + v.Type = "extmsg.group_created" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeExtmsgInbound returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeExtmsgInbound +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeExtmsgInbound() (TypedEventStreamEnvelopeExtmsgInbound, error) { + var body TypedEventStreamEnvelopeExtmsgInbound + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeExtmsgInbound overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeExtmsgInbound +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeExtmsgInbound(v TypedEventStreamEnvelopeExtmsgInbound) error { + v.Type = "extmsg.inbound" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeExtmsgInbound performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeExtmsgInbound +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeExtmsgInbound(v TypedEventStreamEnvelopeExtmsgInbound) error { + v.Type = "extmsg.inbound" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeExtmsgOutbound returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeExtmsgOutbound +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeExtmsgOutbound() (TypedEventStreamEnvelopeExtmsgOutbound, error) { + var body TypedEventStreamEnvelopeExtmsgOutbound + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeExtmsgOutbound overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeExtmsgOutbound +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeExtmsgOutbound(v TypedEventStreamEnvelopeExtmsgOutbound) error { + v.Type = "extmsg.outbound" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeExtmsgOutbound performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeExtmsgOutbound +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeExtmsgOutbound(v TypedEventStreamEnvelopeExtmsgOutbound) error { + v.Type = "extmsg.outbound" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeExtmsgUnbound returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeExtmsgUnbound +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeExtmsgUnbound() (TypedEventStreamEnvelopeExtmsgUnbound, error) { + var body TypedEventStreamEnvelopeExtmsgUnbound + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeExtmsgUnbound overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeExtmsgUnbound +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeExtmsgUnbound(v TypedEventStreamEnvelopeExtmsgUnbound) error { + v.Type = "extmsg.unbound" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeExtmsgUnbound performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeExtmsgUnbound +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeExtmsgUnbound(v TypedEventStreamEnvelopeExtmsgUnbound) error { + v.Type = "extmsg.unbound" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeMailArchived returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeMailArchived +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeMailArchived() (TypedEventStreamEnvelopeMailArchived, error) { + var body TypedEventStreamEnvelopeMailArchived + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeMailArchived overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeMailArchived +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeMailArchived(v TypedEventStreamEnvelopeMailArchived) error { + v.Type = "mail.archived" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeMailArchived performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeMailArchived +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeMailArchived(v TypedEventStreamEnvelopeMailArchived) error { + v.Type = "mail.archived" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeMailDeleted returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeMailDeleted +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeMailDeleted() (TypedEventStreamEnvelopeMailDeleted, error) { + var body TypedEventStreamEnvelopeMailDeleted + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeMailDeleted overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeMailDeleted +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeMailDeleted(v TypedEventStreamEnvelopeMailDeleted) error { + v.Type = "mail.deleted" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeMailDeleted performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeMailDeleted +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeMailDeleted(v TypedEventStreamEnvelopeMailDeleted) error { + v.Type = "mail.deleted" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeMailMarkedRead returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeMailMarkedRead +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeMailMarkedRead() (TypedEventStreamEnvelopeMailMarkedRead, error) { + var body TypedEventStreamEnvelopeMailMarkedRead + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeMailMarkedRead overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeMailMarkedRead +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeMailMarkedRead(v TypedEventStreamEnvelopeMailMarkedRead) error { + v.Type = "mail.marked_read" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeMailMarkedRead performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeMailMarkedRead +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeMailMarkedRead(v TypedEventStreamEnvelopeMailMarkedRead) error { + v.Type = "mail.marked_read" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeMailMarkedUnread returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeMailMarkedUnread +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeMailMarkedUnread() (TypedEventStreamEnvelopeMailMarkedUnread, error) { + var body TypedEventStreamEnvelopeMailMarkedUnread + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeMailMarkedUnread overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeMailMarkedUnread +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeMailMarkedUnread(v TypedEventStreamEnvelopeMailMarkedUnread) error { + v.Type = "mail.marked_unread" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeMailMarkedUnread performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeMailMarkedUnread +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeMailMarkedUnread(v TypedEventStreamEnvelopeMailMarkedUnread) error { + v.Type = "mail.marked_unread" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeMailRead returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeMailRead +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeMailRead() (TypedEventStreamEnvelopeMailRead, error) { + var body TypedEventStreamEnvelopeMailRead + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeMailRead overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeMailRead +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeMailRead(v TypedEventStreamEnvelopeMailRead) error { + v.Type = "mail.read" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeMailRead performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeMailRead +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeMailRead(v TypedEventStreamEnvelopeMailRead) error { + v.Type = "mail.read" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeMailReplied returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeMailReplied +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeMailReplied() (TypedEventStreamEnvelopeMailReplied, error) { + var body TypedEventStreamEnvelopeMailReplied + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeMailReplied overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeMailReplied +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeMailReplied(v TypedEventStreamEnvelopeMailReplied) error { + v.Type = "mail.replied" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeMailReplied performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeMailReplied +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeMailReplied(v TypedEventStreamEnvelopeMailReplied) error { + v.Type = "mail.replied" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Before Message UUID cursor for loading older messages. - Before *string `form:"before,omitempty" json:"before,omitempty"` +// AsTypedEventStreamEnvelopeMailSent returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeMailSent +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeMailSent() (TypedEventStreamEnvelopeMailSent, error) { + var body TypedEventStreamEnvelopeMailSent + err := json.Unmarshal(t.union, &body) + return body, err } -// PostV0CityByCityNameAgentByDirByBaseByActionParamsAction defines parameters for PostV0CityByCityNameAgentByDirByBaseByAction. -type PostV0CityByCityNameAgentByDirByBaseByActionParamsAction string +// FromTypedEventStreamEnvelopeMailSent overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeMailSent +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeMailSent(v TypedEventStreamEnvelopeMailSent) error { + v.Type = "mail.sent" + b, err := json.Marshal(v) + t.union = b + return err +} -// GetV0CityByCityNameAgentsParams defines parameters for GetV0CityByCityNameAgents. -type GetV0CityByCityNameAgentsParams struct { - // Index Event sequence number; when provided, blocks until a newer event arrives. - Index *string `form:"index,omitempty" json:"index,omitempty"` +// MergeTypedEventStreamEnvelopeMailSent performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeMailSent +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeMailSent(v TypedEventStreamEnvelopeMailSent) error { + v.Type = "mail.sent" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. - Wait *string `form:"wait,omitempty" json:"wait,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Pool Filter by pool name. - Pool *string `form:"pool,omitempty" json:"pool,omitempty"` +// AsTypedEventStreamEnvelopeOrderCompleted returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeOrderCompleted +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeOrderCompleted() (TypedEventStreamEnvelopeOrderCompleted, error) { + var body TypedEventStreamEnvelopeOrderCompleted + err := json.Unmarshal(t.union, &body) + return body, err +} - // Rig Filter by rig name. - Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +// FromTypedEventStreamEnvelopeOrderCompleted overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeOrderCompleted +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeOrderCompleted(v TypedEventStreamEnvelopeOrderCompleted) error { + v.Type = "order.completed" + b, err := json.Marshal(v) + t.union = b + return err +} - // Running Filter by running state. Omit to return all agents. - Running *GetV0CityByCityNameAgentsParamsRunning `form:"running,omitempty" json:"running,omitempty"` +// MergeTypedEventStreamEnvelopeOrderCompleted performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeOrderCompleted +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeOrderCompleted(v TypedEventStreamEnvelopeOrderCompleted) error { + v.Type = "order.completed" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Peek Include last output preview. - Peek *bool `form:"peek,omitempty" json:"peek,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// GetV0CityByCityNameAgentsParamsRunning defines parameters for GetV0CityByCityNameAgents. -type GetV0CityByCityNameAgentsParamsRunning string +// AsTypedEventStreamEnvelopeOrderFailed returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeOrderFailed +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeOrderFailed() (TypedEventStreamEnvelopeOrderFailed, error) { + var body TypedEventStreamEnvelopeOrderFailed + err := json.Unmarshal(t.union, &body) + return body, err +} -// GetV0CityByCityNameBeadsParams defines parameters for GetV0CityByCityNameBeads. -type GetV0CityByCityNameBeadsParams struct { - // Index Event sequence number; when provided, blocks until a newer event arrives. - Index *string `form:"index,omitempty" json:"index,omitempty"` +// FromTypedEventStreamEnvelopeOrderFailed overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeOrderFailed +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeOrderFailed(v TypedEventStreamEnvelopeOrderFailed) error { + v.Type = "order.failed" + b, err := json.Marshal(v) + t.union = b + return err +} - // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. - Wait *string `form:"wait,omitempty" json:"wait,omitempty"` +// MergeTypedEventStreamEnvelopeOrderFailed performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeOrderFailed +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeOrderFailed(v TypedEventStreamEnvelopeOrderFailed) error { + v.Type = "order.failed" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Cursor Pagination cursor from a previous response's next_cursor field. - Cursor *string `form:"cursor,omitempty" json:"cursor,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Limit Maximum number of results to return. 0 = server default. - Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +// AsTypedEventStreamEnvelopeOrderFired returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeOrderFired +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeOrderFired() (TypedEventStreamEnvelopeOrderFired, error) { + var body TypedEventStreamEnvelopeOrderFired + err := json.Unmarshal(t.union, &body) + return body, err +} - // Status Filter by bead status. - Status *string `form:"status,omitempty" json:"status,omitempty"` +// FromTypedEventStreamEnvelopeOrderFired overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeOrderFired +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeOrderFired(v TypedEventStreamEnvelopeOrderFired) error { + v.Type = "order.fired" + b, err := json.Marshal(v) + t.union = b + return err +} - // Type Filter by bead type. - Type *string `form:"type,omitempty" json:"type,omitempty"` +// MergeTypedEventStreamEnvelopeOrderFired performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeOrderFired +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeOrderFired(v TypedEventStreamEnvelopeOrderFired) error { + v.Type = "order.fired" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Label Filter by label. - Label *string `form:"label,omitempty" json:"label,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Assignee Filter by assignee. - Assignee *string `form:"assignee,omitempty" json:"assignee,omitempty"` +// AsTypedEventStreamEnvelopeProviderSwapped returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeProviderSwapped +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeProviderSwapped() (TypedEventStreamEnvelopeProviderSwapped, error) { + var body TypedEventStreamEnvelopeProviderSwapped + err := json.Unmarshal(t.union, &body) + return body, err +} - // Rig Filter by rig. - Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +// FromTypedEventStreamEnvelopeProviderSwapped overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeProviderSwapped +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeProviderSwapped(v TypedEventStreamEnvelopeProviderSwapped) error { + v.Type = "provider.swapped" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeProviderSwapped performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeProviderSwapped +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeProviderSwapped(v TypedEventStreamEnvelopeProviderSwapped) error { + v.Type = "provider.swapped" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeSessionCrashed returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeSessionCrashed +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeSessionCrashed() (TypedEventStreamEnvelopeSessionCrashed, error) { + var body TypedEventStreamEnvelopeSessionCrashed + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeSessionCrashed overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeSessionCrashed +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeSessionCrashed(v TypedEventStreamEnvelopeSessionCrashed) error { + v.Type = "session.crashed" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeSessionCrashed performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeSessionCrashed +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeSessionCrashed(v TypedEventStreamEnvelopeSessionCrashed) error { + v.Type = "session.crashed" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeSessionDraining returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeSessionDraining +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeSessionDraining() (TypedEventStreamEnvelopeSessionDraining, error) { + var body TypedEventStreamEnvelopeSessionDraining + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeSessionDraining overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeSessionDraining +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeSessionDraining(v TypedEventStreamEnvelopeSessionDraining) error { + v.Type = "session.draining" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeSessionDraining performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeSessionDraining +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeSessionDraining(v TypedEventStreamEnvelopeSessionDraining) error { + v.Type = "session.draining" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeSessionIdleKilled returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeSessionIdleKilled +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeSessionIdleKilled() (TypedEventStreamEnvelopeSessionIdleKilled, error) { + var body TypedEventStreamEnvelopeSessionIdleKilled + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeSessionIdleKilled overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeSessionIdleKilled +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeSessionIdleKilled(v TypedEventStreamEnvelopeSessionIdleKilled) error { + v.Type = "session.idle_killed" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeSessionIdleKilled performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeSessionIdleKilled +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeSessionIdleKilled(v TypedEventStreamEnvelopeSessionIdleKilled) error { + v.Type = "session.idle_killed" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeSessionQuarantined returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeSessionQuarantined +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeSessionQuarantined() (TypedEventStreamEnvelopeSessionQuarantined, error) { + var body TypedEventStreamEnvelopeSessionQuarantined + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeSessionQuarantined overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeSessionQuarantined +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeSessionQuarantined(v TypedEventStreamEnvelopeSessionQuarantined) error { + v.Type = "session.quarantined" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeSessionQuarantined performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeSessionQuarantined +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeSessionQuarantined(v TypedEventStreamEnvelopeSessionQuarantined) error { + v.Type = "session.quarantined" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeSessionStopped returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeSessionStopped +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeSessionStopped() (TypedEventStreamEnvelopeSessionStopped, error) { + var body TypedEventStreamEnvelopeSessionStopped + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeSessionStopped overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeSessionStopped +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeSessionStopped(v TypedEventStreamEnvelopeSessionStopped) error { + v.Type = "session.stopped" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeSessionStopped performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeSessionStopped +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeSessionStopped(v TypedEventStreamEnvelopeSessionStopped) error { + v.Type = "session.stopped" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeSessionSuspended returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeSessionSuspended +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeSessionSuspended() (TypedEventStreamEnvelopeSessionSuspended, error) { + var body TypedEventStreamEnvelopeSessionSuspended + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeSessionSuspended overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeSessionSuspended +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeSessionSuspended(v TypedEventStreamEnvelopeSessionSuspended) error { + v.Type = "session.suspended" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeSessionSuspended performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeSessionSuspended +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeSessionSuspended(v TypedEventStreamEnvelopeSessionSuspended) error { + v.Type = "session.suspended" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedEventStreamEnvelopeSessionUndrained returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeSessionUndrained +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeSessionUndrained() (TypedEventStreamEnvelopeSessionUndrained, error) { + var body TypedEventStreamEnvelopeSessionUndrained + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedEventStreamEnvelopeSessionUndrained overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeSessionUndrained +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeSessionUndrained(v TypedEventStreamEnvelopeSessionUndrained) error { + v.Type = "session.undrained" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedEventStreamEnvelopeSessionUndrained performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeSessionUndrained +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeSessionUndrained(v TypedEventStreamEnvelopeSessionUndrained) error { + v.Type = "session.undrained" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// CreateBeadParams defines parameters for CreateBead. -type CreateBeadParams struct { - // IdempotencyKey Idempotency key for safe retries. - IdempotencyKey *string `json:"Idempotency-Key,omitempty"` +// AsTypedEventStreamEnvelopeSessionUpdated returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeSessionUpdated +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeSessionUpdated() (TypedEventStreamEnvelopeSessionUpdated, error) { + var body TypedEventStreamEnvelopeSessionUpdated + err := json.Unmarshal(t.union, &body) + return body, err } -// GetV0CityByCityNameBeadsReadyParams defines parameters for GetV0CityByCityNameBeadsReady. -type GetV0CityByCityNameBeadsReadyParams struct { - // Index Event sequence number; when provided, blocks until a newer event arrives. - Index *string `form:"index,omitempty" json:"index,omitempty"` - - // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. - Wait *string `form:"wait,omitempty" json:"wait,omitempty"` +// FromTypedEventStreamEnvelopeSessionUpdated overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeSessionUpdated +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeSessionUpdated(v TypedEventStreamEnvelopeSessionUpdated) error { + v.Type = "session.updated" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameConvoysParams defines parameters for GetV0CityByCityNameConvoys. -type GetV0CityByCityNameConvoysParams struct { - // Index Event sequence number; when provided, blocks until a newer event arrives. - Index *string `form:"index,omitempty" json:"index,omitempty"` +// MergeTypedEventStreamEnvelopeSessionUpdated performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeSessionUpdated +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeSessionUpdated(v TypedEventStreamEnvelopeSessionUpdated) error { + v.Type = "session.updated" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. - Wait *string `form:"wait,omitempty" json:"wait,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Cursor Pagination cursor from a previous response's next_cursor field. - Cursor *string `form:"cursor,omitempty" json:"cursor,omitempty"` +// AsTypedEventStreamEnvelopeSessionWoke returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeSessionWoke +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeSessionWoke() (TypedEventStreamEnvelopeSessionWoke, error) { + var body TypedEventStreamEnvelopeSessionWoke + err := json.Unmarshal(t.union, &body) + return body, err +} - // Limit Maximum number of results to return. 0 = server default. - Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +// FromTypedEventStreamEnvelopeSessionWoke overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeSessionWoke +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeSessionWoke(v TypedEventStreamEnvelopeSessionWoke) error { + v.Type = "session.woke" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameEventsParams defines parameters for GetV0CityByCityNameEvents. -type GetV0CityByCityNameEventsParams struct { - // Index Event sequence number; when provided, blocks until a newer event arrives. - Index *string `form:"index,omitempty" json:"index,omitempty"` +// MergeTypedEventStreamEnvelopeSessionWoke performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeSessionWoke +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeSessionWoke(v TypedEventStreamEnvelopeSessionWoke) error { + v.Type = "session.woke" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. - Wait *string `form:"wait,omitempty" json:"wait,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Cursor Pagination cursor from a previous response's next_cursor field. - Cursor *string `form:"cursor,omitempty" json:"cursor,omitempty"` +// AsTypedEventStreamEnvelopeWorkerOperation returns the union data inside the TypedEventStreamEnvelope as a TypedEventStreamEnvelopeWorkerOperation +func (t TypedEventStreamEnvelope) AsTypedEventStreamEnvelopeWorkerOperation() (TypedEventStreamEnvelopeWorkerOperation, error) { + var body TypedEventStreamEnvelopeWorkerOperation + err := json.Unmarshal(t.union, &body) + return body, err +} - // Limit Maximum number of results to return. 0 = server default. - Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +// FromTypedEventStreamEnvelopeWorkerOperation overwrites any union data inside the TypedEventStreamEnvelope as the provided TypedEventStreamEnvelopeWorkerOperation +func (t *TypedEventStreamEnvelope) FromTypedEventStreamEnvelopeWorkerOperation(v TypedEventStreamEnvelopeWorkerOperation) error { + v.Type = "worker.operation" + b, err := json.Marshal(v) + t.union = b + return err +} - // Type Filter by event type. - Type *string `form:"type,omitempty" json:"type,omitempty"` +// MergeTypedEventStreamEnvelopeWorkerOperation performs a merge with any union data inside the TypedEventStreamEnvelope, using the provided TypedEventStreamEnvelopeWorkerOperation +func (t *TypedEventStreamEnvelope) MergeTypedEventStreamEnvelopeWorkerOperation(v TypedEventStreamEnvelopeWorkerOperation) error { + v.Type = "worker.operation" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Actor Filter by actor. - Actor *string `form:"actor,omitempty" json:"actor,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Since Filter events since duration ago (Go duration string, e.g. 5m). - Since *string `form:"since,omitempty" json:"since,omitempty"` +func (t TypedEventStreamEnvelope) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"type"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t TypedEventStreamEnvelope) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "bead.closed": + return t.AsTypedEventStreamEnvelopeBeadClosed() + case "bead.created": + return t.AsTypedEventStreamEnvelopeBeadCreated() + case "bead.updated": + return t.AsTypedEventStreamEnvelopeBeadUpdated() + case "city.created": + return t.AsTypedEventStreamEnvelopeCityCreated() + case "city.init_failed": + return t.AsTypedEventStreamEnvelopeCityInitFailed() + case "city.ready": + return t.AsTypedEventStreamEnvelopeCityReady() + case "city.resumed": + return t.AsTypedEventStreamEnvelopeCityResumed() + case "city.suspended": + return t.AsTypedEventStreamEnvelopeCitySuspended() + case "city.unregister_failed": + return t.AsTypedEventStreamEnvelopeCityUnregisterFailed() + case "city.unregister_requested": + return t.AsTypedEventStreamEnvelopeCityUnregisterRequested() + case "city.unregistered": + return t.AsTypedEventStreamEnvelopeCityUnregistered() + case "controller.started": + return t.AsTypedEventStreamEnvelopeControllerStarted() + case "controller.stopped": + return t.AsTypedEventStreamEnvelopeControllerStopped() + case "convoy.closed": + return t.AsTypedEventStreamEnvelopeConvoyClosed() + case "convoy.created": + return t.AsTypedEventStreamEnvelopeConvoyCreated() + case "extmsg.adapter_added": + return t.AsTypedEventStreamEnvelopeExtmsgAdapterAdded() + case "extmsg.adapter_removed": + return t.AsTypedEventStreamEnvelopeExtmsgAdapterRemoved() + case "extmsg.bound": + return t.AsTypedEventStreamEnvelopeExtmsgBound() + case "extmsg.group_created": + return t.AsTypedEventStreamEnvelopeExtmsgGroupCreated() + case "extmsg.inbound": + return t.AsTypedEventStreamEnvelopeExtmsgInbound() + case "extmsg.outbound": + return t.AsTypedEventStreamEnvelopeExtmsgOutbound() + case "extmsg.unbound": + return t.AsTypedEventStreamEnvelopeExtmsgUnbound() + case "mail.archived": + return t.AsTypedEventStreamEnvelopeMailArchived() + case "mail.deleted": + return t.AsTypedEventStreamEnvelopeMailDeleted() + case "mail.marked_read": + return t.AsTypedEventStreamEnvelopeMailMarkedRead() + case "mail.marked_unread": + return t.AsTypedEventStreamEnvelopeMailMarkedUnread() + case "mail.read": + return t.AsTypedEventStreamEnvelopeMailRead() + case "mail.replied": + return t.AsTypedEventStreamEnvelopeMailReplied() + case "mail.sent": + return t.AsTypedEventStreamEnvelopeMailSent() + case "order.completed": + return t.AsTypedEventStreamEnvelopeOrderCompleted() + case "order.failed": + return t.AsTypedEventStreamEnvelopeOrderFailed() + case "order.fired": + return t.AsTypedEventStreamEnvelopeOrderFired() + case "provider.swapped": + return t.AsTypedEventStreamEnvelopeProviderSwapped() + case "session.crashed": + return t.AsTypedEventStreamEnvelopeSessionCrashed() + case "session.draining": + return t.AsTypedEventStreamEnvelopeSessionDraining() + case "session.idle_killed": + return t.AsTypedEventStreamEnvelopeSessionIdleKilled() + case "session.quarantined": + return t.AsTypedEventStreamEnvelopeSessionQuarantined() + case "session.stopped": + return t.AsTypedEventStreamEnvelopeSessionStopped() + case "session.suspended": + return t.AsTypedEventStreamEnvelopeSessionSuspended() + case "session.undrained": + return t.AsTypedEventStreamEnvelopeSessionUndrained() + case "session.updated": + return t.AsTypedEventStreamEnvelopeSessionUpdated() + case "session.woke": + return t.AsTypedEventStreamEnvelopeSessionWoke() + case "worker.operation": + return t.AsTypedEventStreamEnvelopeWorkerOperation() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } } -// StreamEventsParams defines parameters for StreamEvents. -type StreamEventsParams struct { - // AfterSeq Reconnect position: only deliver events after this sequence number. - AfterSeq *string `form:"after_seq,omitempty" json:"after_seq,omitempty"` +func (t TypedEventStreamEnvelope) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} - // LastEventID SSE reconnect position from the last received event ID. - LastEventID *string `json:"Last-Event-ID,omitempty"` +func (t *TypedEventStreamEnvelope) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err } -// GetV0CityByCityNameExtmsgBindingsParams defines parameters for GetV0CityByCityNameExtmsgBindings. -type GetV0CityByCityNameExtmsgBindingsParams struct { - // SessionId Session ID to list bindings for. - SessionId *string `form:"session_id,omitempty" json:"session_id,omitempty"` +// AsTypedTaggedEventStreamEnvelopeBeadClosed returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeBeadClosed +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeBeadClosed() (TypedTaggedEventStreamEnvelopeBeadClosed, error) { + var body TypedTaggedEventStreamEnvelopeBeadClosed + err := json.Unmarshal(t.union, &body) + return body, err } -// GetV0CityByCityNameExtmsgGroupsParams defines parameters for GetV0CityByCityNameExtmsgGroups. -type GetV0CityByCityNameExtmsgGroupsParams struct { - // ScopeId Scope ID. - ScopeId *string `form:"scope_id,omitempty" json:"scope_id,omitempty"` +// FromTypedTaggedEventStreamEnvelopeBeadClosed overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeBeadClosed +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeBeadClosed(v TypedTaggedEventStreamEnvelopeBeadClosed) error { + v.Type = "bead.closed" + b, err := json.Marshal(v) + t.union = b + return err +} - // Provider Provider name. - Provider *string `form:"provider,omitempty" json:"provider,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeBeadClosed performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeBeadClosed +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeBeadClosed(v TypedTaggedEventStreamEnvelopeBeadClosed) error { + v.Type = "bead.closed" + b, err := json.Marshal(v) + if err != nil { + return err + } - // AccountId Account ID. - AccountId *string `form:"account_id,omitempty" json:"account_id,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // ConversationId Conversation ID. - ConversationId *string `form:"conversation_id,omitempty" json:"conversation_id,omitempty"` +// AsTypedTaggedEventStreamEnvelopeBeadCreated returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeBeadCreated +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeBeadCreated() (TypedTaggedEventStreamEnvelopeBeadCreated, error) { + var body TypedTaggedEventStreamEnvelopeBeadCreated + err := json.Unmarshal(t.union, &body) + return body, err +} - // Kind Conversation kind. - Kind *string `form:"kind,omitempty" json:"kind,omitempty"` +// FromTypedTaggedEventStreamEnvelopeBeadCreated overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeBeadCreated +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeBeadCreated(v TypedTaggedEventStreamEnvelopeBeadCreated) error { + v.Type = "bead.created" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameExtmsgTranscriptParams defines parameters for GetV0CityByCityNameExtmsgTranscript. -type GetV0CityByCityNameExtmsgTranscriptParams struct { - // ScopeId Scope ID. - ScopeId *string `form:"scope_id,omitempty" json:"scope_id,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeBeadCreated performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeBeadCreated +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeBeadCreated(v TypedTaggedEventStreamEnvelopeBeadCreated) error { + v.Type = "bead.created" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Provider Provider name. - Provider *string `form:"provider,omitempty" json:"provider,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // AccountId Account ID. - AccountId *string `form:"account_id,omitempty" json:"account_id,omitempty"` +// AsTypedTaggedEventStreamEnvelopeBeadUpdated returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeBeadUpdated +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeBeadUpdated() (TypedTaggedEventStreamEnvelopeBeadUpdated, error) { + var body TypedTaggedEventStreamEnvelopeBeadUpdated + err := json.Unmarshal(t.union, &body) + return body, err +} - // ConversationId Conversation ID. - ConversationId *string `form:"conversation_id,omitempty" json:"conversation_id,omitempty"` +// FromTypedTaggedEventStreamEnvelopeBeadUpdated overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeBeadUpdated +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeBeadUpdated(v TypedTaggedEventStreamEnvelopeBeadUpdated) error { + v.Type = "bead.updated" + b, err := json.Marshal(v) + t.union = b + return err +} - // ParentConversationId Parent conversation ID. - ParentConversationId *string `form:"parent_conversation_id,omitempty" json:"parent_conversation_id,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeBeadUpdated performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeBeadUpdated +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeBeadUpdated(v TypedTaggedEventStreamEnvelopeBeadUpdated) error { + v.Type = "bead.updated" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Kind Conversation kind. - Kind *string `form:"kind,omitempty" json:"kind,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// GetV0CityByCityNameFormulaByNameParams defines parameters for GetV0CityByCityNameFormulaByName. -type GetV0CityByCityNameFormulaByNameParams struct { - // ScopeKind Scope kind (city or rig). - ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` +// AsTypedTaggedEventStreamEnvelopeCityCreated returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeCityCreated +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeCityCreated() (TypedTaggedEventStreamEnvelopeCityCreated, error) { + var body TypedTaggedEventStreamEnvelopeCityCreated + err := json.Unmarshal(t.union, &body) + return body, err +} - // ScopeRef Scope reference. - ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` +// FromTypedTaggedEventStreamEnvelopeCityCreated overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeCityCreated +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeCityCreated(v TypedTaggedEventStreamEnvelopeCityCreated) error { + v.Type = "city.created" + b, err := json.Marshal(v) + t.union = b + return err +} - // Target Target agent for preview compilation. - Target string `form:"target" json:"target"` +// MergeTypedTaggedEventStreamEnvelopeCityCreated performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeCityCreated +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeCityCreated(v TypedTaggedEventStreamEnvelopeCityCreated) error { + v.Type = "city.created" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// GetV0CityByCityNameFormulasParams defines parameters for GetV0CityByCityNameFormulas. -type GetV0CityByCityNameFormulasParams struct { - // ScopeKind Scope kind (city or rig). - ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` +// AsTypedTaggedEventStreamEnvelopeCityInitFailed returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeCityInitFailed +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeCityInitFailed() (TypedTaggedEventStreamEnvelopeCityInitFailed, error) { + var body TypedTaggedEventStreamEnvelopeCityInitFailed + err := json.Unmarshal(t.union, &body) + return body, err +} - // ScopeRef Scope reference. - ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` +// FromTypedTaggedEventStreamEnvelopeCityInitFailed overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeCityInitFailed +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeCityInitFailed(v TypedTaggedEventStreamEnvelopeCityInitFailed) error { + v.Type = "city.init_failed" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameFormulasFeedParams defines parameters for GetV0CityByCityNameFormulasFeed. -type GetV0CityByCityNameFormulasFeedParams struct { - // ScopeKind Scope kind (city or rig). - ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeCityInitFailed performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeCityInitFailed +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeCityInitFailed(v TypedTaggedEventStreamEnvelopeCityInitFailed) error { + v.Type = "city.init_failed" + b, err := json.Marshal(v) + if err != nil { + return err + } - // ScopeRef Scope reference. - ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Limit Maximum number of feed items to return. 0 = default. - Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +// AsTypedTaggedEventStreamEnvelopeCityReady returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeCityReady +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeCityReady() (TypedTaggedEventStreamEnvelopeCityReady, error) { + var body TypedTaggedEventStreamEnvelopeCityReady + err := json.Unmarshal(t.union, &body) + return body, err } -// GetV0CityByCityNameFormulasByNameParams defines parameters for GetV0CityByCityNameFormulasByName. -type GetV0CityByCityNameFormulasByNameParams struct { - // ScopeKind Scope kind (city or rig). - ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` +// FromTypedTaggedEventStreamEnvelopeCityReady overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeCityReady +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeCityReady(v TypedTaggedEventStreamEnvelopeCityReady) error { + v.Type = "city.ready" + b, err := json.Marshal(v) + t.union = b + return err +} - // ScopeRef Scope reference. - ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeCityReady performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeCityReady +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeCityReady(v TypedTaggedEventStreamEnvelopeCityReady) error { + v.Type = "city.ready" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Target Target agent for preview compilation. - Target string `form:"target" json:"target"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// GetV0CityByCityNameFormulasByNameRunsParams defines parameters for GetV0CityByCityNameFormulasByNameRuns. -type GetV0CityByCityNameFormulasByNameRunsParams struct { - // ScopeKind Scope kind (city or rig). - ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` +// AsTypedTaggedEventStreamEnvelopeCityResumed returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeCityResumed +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeCityResumed() (TypedTaggedEventStreamEnvelopeCityResumed, error) { + var body TypedTaggedEventStreamEnvelopeCityResumed + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTypedTaggedEventStreamEnvelopeCityResumed overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeCityResumed +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeCityResumed(v TypedTaggedEventStreamEnvelopeCityResumed) error { + v.Type = "city.resumed" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedTaggedEventStreamEnvelopeCityResumed performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeCityResumed +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeCityResumed(v TypedTaggedEventStreamEnvelopeCityResumed) error { + v.Type = "city.resumed" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // ScopeRef Scope reference. - ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` +// AsTypedTaggedEventStreamEnvelopeCitySuspended returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeCitySuspended +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeCitySuspended() (TypedTaggedEventStreamEnvelopeCitySuspended, error) { + var body TypedTaggedEventStreamEnvelopeCitySuspended + err := json.Unmarshal(t.union, &body) + return body, err +} - // Limit Maximum number of recent runs to return. 0 = default. - Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +// FromTypedTaggedEventStreamEnvelopeCitySuspended overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeCitySuspended +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeCitySuspended(v TypedTaggedEventStreamEnvelopeCitySuspended) error { + v.Type = "city.suspended" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameMailParams defines parameters for GetV0CityByCityNameMail. -type GetV0CityByCityNameMailParams struct { - // Index Event sequence number; when provided, blocks until a newer event arrives. - Index *string `form:"index,omitempty" json:"index,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeCitySuspended performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeCitySuspended +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeCitySuspended(v TypedTaggedEventStreamEnvelopeCitySuspended) error { + v.Type = "city.suspended" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. - Wait *string `form:"wait,omitempty" json:"wait,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Cursor Pagination cursor from a previous response's next_cursor field. - Cursor *string `form:"cursor,omitempty" json:"cursor,omitempty"` +// AsTypedTaggedEventStreamEnvelopeCityUnregisterFailed returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeCityUnregisterFailed +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeCityUnregisterFailed() (TypedTaggedEventStreamEnvelopeCityUnregisterFailed, error) { + var body TypedTaggedEventStreamEnvelopeCityUnregisterFailed + err := json.Unmarshal(t.union, &body) + return body, err +} - // Limit Maximum number of results to return. 0 = server default. - Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +// FromTypedTaggedEventStreamEnvelopeCityUnregisterFailed overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeCityUnregisterFailed +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeCityUnregisterFailed(v TypedTaggedEventStreamEnvelopeCityUnregisterFailed) error { + v.Type = "city.unregister_failed" + b, err := json.Marshal(v) + t.union = b + return err +} - // Agent Filter by agent name. - Agent *string `form:"agent,omitempty" json:"agent,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeCityUnregisterFailed performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeCityUnregisterFailed +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeCityUnregisterFailed(v TypedTaggedEventStreamEnvelopeCityUnregisterFailed) error { + v.Type = "city.unregister_failed" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Status Filter by status (unread, all). - Status *string `form:"status,omitempty" json:"status,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Rig Filter by rig name. - Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +// AsTypedTaggedEventStreamEnvelopeCityUnregisterRequested returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeCityUnregisterRequested +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeCityUnregisterRequested() (TypedTaggedEventStreamEnvelopeCityUnregisterRequested, error) { + var body TypedTaggedEventStreamEnvelopeCityUnregisterRequested + err := json.Unmarshal(t.union, &body) + return body, err } -// SendMailParams defines parameters for SendMail. -type SendMailParams struct { - // IdempotencyKey Idempotency key for safe retries. - IdempotencyKey *string `json:"Idempotency-Key,omitempty"` +// FromTypedTaggedEventStreamEnvelopeCityUnregisterRequested overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeCityUnregisterRequested +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeCityUnregisterRequested(v TypedTaggedEventStreamEnvelopeCityUnregisterRequested) error { + v.Type = "city.unregister_requested" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameMailCountParams defines parameters for GetV0CityByCityNameMailCount. -type GetV0CityByCityNameMailCountParams struct { - // Agent Filter by agent name. - Agent *string `form:"agent,omitempty" json:"agent,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeCityUnregisterRequested performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeCityUnregisterRequested +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeCityUnregisterRequested(v TypedTaggedEventStreamEnvelopeCityUnregisterRequested) error { + v.Type = "city.unregister_requested" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Rig Filter by rig name. - Rig *string `form:"rig,omitempty" json:"rig,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// GetV0CityByCityNameMailThreadByIdParams defines parameters for GetV0CityByCityNameMailThreadById. -type GetV0CityByCityNameMailThreadByIdParams struct { - // Rig Filter by rig. - Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +// AsTypedTaggedEventStreamEnvelopeCityUnregistered returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeCityUnregistered +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeCityUnregistered() (TypedTaggedEventStreamEnvelopeCityUnregistered, error) { + var body TypedTaggedEventStreamEnvelopeCityUnregistered + err := json.Unmarshal(t.union, &body) + return body, err } -// DeleteV0CityByCityNameMailByIdParams defines parameters for DeleteV0CityByCityNameMailById. -type DeleteV0CityByCityNameMailByIdParams struct { - // Rig Rig hint. - Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +// FromTypedTaggedEventStreamEnvelopeCityUnregistered overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeCityUnregistered +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeCityUnregistered(v TypedTaggedEventStreamEnvelopeCityUnregistered) error { + v.Type = "city.unregistered" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameMailByIdParams defines parameters for GetV0CityByCityNameMailById. -type GetV0CityByCityNameMailByIdParams struct { - // Rig Rig hint for O(1) lookup. - Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeCityUnregistered performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeCityUnregistered +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeCityUnregistered(v TypedTaggedEventStreamEnvelopeCityUnregistered) error { + v.Type = "city.unregistered" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// PostV0CityByCityNameMailByIdArchiveParams defines parameters for PostV0CityByCityNameMailByIdArchive. -type PostV0CityByCityNameMailByIdArchiveParams struct { - // Rig Rig hint. - Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +// AsTypedTaggedEventStreamEnvelopeControllerStarted returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeControllerStarted +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeControllerStarted() (TypedTaggedEventStreamEnvelopeControllerStarted, error) { + var body TypedTaggedEventStreamEnvelopeControllerStarted + err := json.Unmarshal(t.union, &body) + return body, err } -// PostV0CityByCityNameMailByIdMarkUnreadParams defines parameters for PostV0CityByCityNameMailByIdMarkUnread. -type PostV0CityByCityNameMailByIdMarkUnreadParams struct { - // Rig Rig hint. - Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +// FromTypedTaggedEventStreamEnvelopeControllerStarted overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeControllerStarted +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeControllerStarted(v TypedTaggedEventStreamEnvelopeControllerStarted) error { + v.Type = "controller.started" + b, err := json.Marshal(v) + t.union = b + return err } -// PostV0CityByCityNameMailByIdReadParams defines parameters for PostV0CityByCityNameMailByIdRead. -type PostV0CityByCityNameMailByIdReadParams struct { - // Rig Rig hint. - Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeControllerStarted performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeControllerStarted +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeControllerStarted(v TypedTaggedEventStreamEnvelopeControllerStarted) error { + v.Type = "controller.started" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// ReplyMailParams defines parameters for ReplyMail. -type ReplyMailParams struct { - // Rig Rig hint. - Rig *string `form:"rig,omitempty" json:"rig,omitempty"` +// AsTypedTaggedEventStreamEnvelopeControllerStopped returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeControllerStopped +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeControllerStopped() (TypedTaggedEventStreamEnvelopeControllerStopped, error) { + var body TypedTaggedEventStreamEnvelopeControllerStopped + err := json.Unmarshal(t.union, &body) + return body, err } -// GetV0CityByCityNameOrderHistoryByBeadIdParams defines parameters for GetV0CityByCityNameOrderHistoryByBeadId. -type GetV0CityByCityNameOrderHistoryByBeadIdParams struct { - // StoreRef Store reference for disambiguating store-local bead IDs. - StoreRef *string `form:"store_ref,omitempty" json:"store_ref,omitempty"` +// FromTypedTaggedEventStreamEnvelopeControllerStopped overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeControllerStopped +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeControllerStopped(v TypedTaggedEventStreamEnvelopeControllerStopped) error { + v.Type = "controller.stopped" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameOrdersFeedParams defines parameters for GetV0CityByCityNameOrdersFeed. -type GetV0CityByCityNameOrdersFeedParams struct { - // ScopeKind Scope kind (city or rig). - ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeControllerStopped performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeControllerStopped +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeControllerStopped(v TypedTaggedEventStreamEnvelopeControllerStopped) error { + v.Type = "controller.stopped" + b, err := json.Marshal(v) + if err != nil { + return err + } - // ScopeRef Scope reference. - ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Limit Maximum number of feed items to return. - Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +// AsTypedTaggedEventStreamEnvelopeConvoyClosed returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeConvoyClosed +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeConvoyClosed() (TypedTaggedEventStreamEnvelopeConvoyClosed, error) { + var body TypedTaggedEventStreamEnvelopeConvoyClosed + err := json.Unmarshal(t.union, &body) + return body, err } -// GetV0CityByCityNameOrdersHistoryParams defines parameters for GetV0CityByCityNameOrdersHistory. -type GetV0CityByCityNameOrdersHistoryParams struct { - // ScopedName Scoped order name. - ScopedName string `form:"scoped_name" json:"scoped_name"` +// FromTypedTaggedEventStreamEnvelopeConvoyClosed overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeConvoyClosed +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeConvoyClosed(v TypedTaggedEventStreamEnvelopeConvoyClosed) error { + v.Type = "convoy.closed" + b, err := json.Marshal(v) + t.union = b + return err +} - // Limit Maximum number of history entries. 0 = default. - Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeConvoyClosed performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeConvoyClosed +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeConvoyClosed(v TypedTaggedEventStreamEnvelopeConvoyClosed) error { + v.Type = "convoy.closed" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Before Return entries before this RFC3339 timestamp. - Before *string `form:"before,omitempty" json:"before,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// GetV0CityByCityNameProviderReadinessParams defines parameters for GetV0CityByCityNameProviderReadiness. -type GetV0CityByCityNameProviderReadinessParams struct { - // Providers Comma-separated provider names to check (default: claude,codex,gemini). - Providers *string `form:"providers,omitempty" json:"providers,omitempty"` +// AsTypedTaggedEventStreamEnvelopeConvoyCreated returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeConvoyCreated +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeConvoyCreated() (TypedTaggedEventStreamEnvelopeConvoyCreated, error) { + var body TypedTaggedEventStreamEnvelopeConvoyCreated + err := json.Unmarshal(t.union, &body) + return body, err +} - // Fresh Force fresh probe, bypassing cache. - Fresh *bool `form:"fresh,omitempty" json:"fresh,omitempty"` +// FromTypedTaggedEventStreamEnvelopeConvoyCreated overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeConvoyCreated +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeConvoyCreated(v TypedTaggedEventStreamEnvelopeConvoyCreated) error { + v.Type = "convoy.created" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameReadinessParams defines parameters for GetV0CityByCityNameReadiness. -type GetV0CityByCityNameReadinessParams struct { - // Items Comma-separated readiness items to check (default: claude,codex,gemini,github_cli). - Items *string `form:"items,omitempty" json:"items,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeConvoyCreated performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeConvoyCreated +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeConvoyCreated(v TypedTaggedEventStreamEnvelopeConvoyCreated) error { + v.Type = "convoy.created" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Fresh Force fresh probe, bypassing cache. - Fresh *bool `form:"fresh,omitempty" json:"fresh,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// GetV0CityByCityNameRigByNameParams defines parameters for GetV0CityByCityNameRigByName. -type GetV0CityByCityNameRigByNameParams struct { - // Git Include git status. - Git *bool `form:"git,omitempty" json:"git,omitempty"` +// AsTypedTaggedEventStreamEnvelopeExtmsgAdapterAdded returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeExtmsgAdapterAdded() (TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded, error) { + var body TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded + err := json.Unmarshal(t.union, &body) + return body, err } -// GetV0CityByCityNameRigsParams defines parameters for GetV0CityByCityNameRigs. -type GetV0CityByCityNameRigsParams struct { - // Index Event sequence number; when provided, blocks until a newer event arrives. - Index *string `form:"index,omitempty" json:"index,omitempty"` - - // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. - Wait *string `form:"wait,omitempty" json:"wait,omitempty"` - - // Git Include git status. - Git *bool `form:"git,omitempty" json:"git,omitempty"` +// FromTypedTaggedEventStreamEnvelopeExtmsgAdapterAdded overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeExtmsgAdapterAdded(v TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded) error { + v.Type = "extmsg.adapter_added" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameSessionByIdParams defines parameters for GetV0CityByCityNameSessionById. -type GetV0CityByCityNameSessionByIdParams struct { - // Peek Include last output preview. - Peek *bool `form:"peek,omitempty" json:"peek,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeExtmsgAdapterAdded performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeExtmsgAdapterAdded(v TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded) error { + v.Type = "extmsg.adapter_added" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// PostV0CityByCityNameSessionByIdCloseParams defines parameters for PostV0CityByCityNameSessionByIdClose. -type PostV0CityByCityNameSessionByIdCloseParams struct { - // Delete Permanently delete bead after closing. - Delete *bool `form:"delete,omitempty" json:"delete,omitempty"` +// AsTypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved() (TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved, error) { + var body TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved + err := json.Unmarshal(t.union, &body) + return body, err } -// StreamSessionParams defines parameters for StreamSession. -type StreamSessionParams struct { - // Format Transcript format: conversation (default) or raw. - Format *string `form:"format,omitempty" json:"format,omitempty"` +// FromTypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved(v TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved) error { + v.Type = "extmsg.adapter_removed" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameSessionByIdTranscriptParams defines parameters for GetV0CityByCityNameSessionByIdTranscript. -type GetV0CityByCityNameSessionByIdTranscriptParams struct { - // Tail Number of recent compaction segments to return. This API parameter keeps compaction-segment semantics even though gc session logs --tail counts displayed transcript entries. Omit for the endpoint default (usually 1); 0 returns all segments; N>0 returns the last N. - Tail *string `form:"tail,omitempty" json:"tail,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved(v TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved) error { + v.Type = "extmsg.adapter_removed" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Format Transcript format: conversation (default) or raw. - Format *string `form:"format,omitempty" json:"format,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Before Pagination cursor: return entries before this UUID. - Before *string `form:"before,omitempty" json:"before,omitempty"` +// AsTypedTaggedEventStreamEnvelopeExtmsgBound returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeExtmsgBound +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeExtmsgBound() (TypedTaggedEventStreamEnvelopeExtmsgBound, error) { + var body TypedTaggedEventStreamEnvelopeExtmsgBound + err := json.Unmarshal(t.union, &body) + return body, err } -// GetV0CityByCityNameSessionsParams defines parameters for GetV0CityByCityNameSessions. -type GetV0CityByCityNameSessionsParams struct { - // Cursor Pagination cursor from a previous response's next_cursor field. - Cursor *string `form:"cursor,omitempty" json:"cursor,omitempty"` +// FromTypedTaggedEventStreamEnvelopeExtmsgBound overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeExtmsgBound +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeExtmsgBound(v TypedTaggedEventStreamEnvelopeExtmsgBound) error { + v.Type = "extmsg.bound" + b, err := json.Marshal(v) + t.union = b + return err +} - // Limit Maximum number of results to return. 0 = server default. - Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeExtmsgBound performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeExtmsgBound +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeExtmsgBound(v TypedTaggedEventStreamEnvelopeExtmsgBound) error { + v.Type = "extmsg.bound" + b, err := json.Marshal(v) + if err != nil { + return err + } - // State Filter by session state (e.g. active, closed). - State *string `form:"state,omitempty" json:"state,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Template Filter by session template (agent qualified name). - Template *string `form:"template,omitempty" json:"template,omitempty"` +// AsTypedTaggedEventStreamEnvelopeExtmsgGroupCreated returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeExtmsgGroupCreated +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeExtmsgGroupCreated() (TypedTaggedEventStreamEnvelopeExtmsgGroupCreated, error) { + var body TypedTaggedEventStreamEnvelopeExtmsgGroupCreated + err := json.Unmarshal(t.union, &body) + return body, err +} - // Peek Include last output preview. - Peek *bool `form:"peek,omitempty" json:"peek,omitempty"` +// FromTypedTaggedEventStreamEnvelopeExtmsgGroupCreated overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeExtmsgGroupCreated +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeExtmsgGroupCreated(v TypedTaggedEventStreamEnvelopeExtmsgGroupCreated) error { + v.Type = "extmsg.group_created" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameStatusParams defines parameters for GetV0CityByCityNameStatus. -type GetV0CityByCityNameStatusParams struct { - // Index Event sequence number; when provided, blocks until a newer event arrives. - Index *string `form:"index,omitempty" json:"index,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeExtmsgGroupCreated performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeExtmsgGroupCreated +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeExtmsgGroupCreated(v TypedTaggedEventStreamEnvelopeExtmsgGroupCreated) error { + v.Type = "extmsg.group_created" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Wait How long to block waiting for changes (Go duration string, e.g. 30s). Default 30s, max 2m. - Wait *string `form:"wait,omitempty" json:"wait,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// DeleteV0CityByCityNameWorkflowByWorkflowIdParams defines parameters for DeleteV0CityByCityNameWorkflowByWorkflowId. -type DeleteV0CityByCityNameWorkflowByWorkflowIdParams struct { - // ScopeKind Scope kind (city or rig). - ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` - - // ScopeRef Scope reference. - ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` +// AsTypedTaggedEventStreamEnvelopeExtmsgInbound returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeExtmsgInbound +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeExtmsgInbound() (TypedTaggedEventStreamEnvelopeExtmsgInbound, error) { + var body TypedTaggedEventStreamEnvelopeExtmsgInbound + err := json.Unmarshal(t.union, &body) + return body, err +} - // Delete Permanently delete beads from store. - Delete *bool `form:"delete,omitempty" json:"delete,omitempty"` +// FromTypedTaggedEventStreamEnvelopeExtmsgInbound overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeExtmsgInbound +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeExtmsgInbound(v TypedTaggedEventStreamEnvelopeExtmsgInbound) error { + v.Type = "extmsg.inbound" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0CityByCityNameWorkflowByWorkflowIdParams defines parameters for GetV0CityByCityNameWorkflowByWorkflowId. -type GetV0CityByCityNameWorkflowByWorkflowIdParams struct { - // ScopeKind Scope kind (city or rig). - ScopeKind *string `form:"scope_kind,omitempty" json:"scope_kind,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeExtmsgInbound performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeExtmsgInbound +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeExtmsgInbound(v TypedTaggedEventStreamEnvelopeExtmsgInbound) error { + v.Type = "extmsg.inbound" + b, err := json.Marshal(v) + if err != nil { + return err + } - // ScopeRef Scope reference. - ScopeRef *string `form:"scope_ref,omitempty" json:"scope_ref,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// GetV0EventsParams defines parameters for GetV0Events. -type GetV0EventsParams struct { - // Type Filter by event type. - Type *string `form:"type,omitempty" json:"type,omitempty"` +// AsTypedTaggedEventStreamEnvelopeExtmsgOutbound returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeExtmsgOutbound +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeExtmsgOutbound() (TypedTaggedEventStreamEnvelopeExtmsgOutbound, error) { + var body TypedTaggedEventStreamEnvelopeExtmsgOutbound + err := json.Unmarshal(t.union, &body) + return body, err +} - // Actor Filter by actor. - Actor *string `form:"actor,omitempty" json:"actor,omitempty"` +// FromTypedTaggedEventStreamEnvelopeExtmsgOutbound overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeExtmsgOutbound +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeExtmsgOutbound(v TypedTaggedEventStreamEnvelopeExtmsgOutbound) error { + v.Type = "extmsg.outbound" + b, err := json.Marshal(v) + t.union = b + return err +} - // Since Filter to events within the last Go duration (e.g. "5m"). - Since *string `form:"since,omitempty" json:"since,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeExtmsgOutbound performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeExtmsgOutbound +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeExtmsgOutbound(v TypedTaggedEventStreamEnvelopeExtmsgOutbound) error { + v.Type = "extmsg.outbound" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Limit Maximum number of trailing events to return. 0 = no limit. Used by 'gc events --seq' to compute the head cursor cheaply. - Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// StreamSupervisorEventsParams defines parameters for StreamSupervisorEvents. -type StreamSupervisorEventsParams struct { - // AfterCursor Alternative to Last-Event-ID for browsers that can't set custom headers. - AfterCursor *string `form:"after_cursor,omitempty" json:"after_cursor,omitempty"` +// AsTypedTaggedEventStreamEnvelopeExtmsgUnbound returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeExtmsgUnbound +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeExtmsgUnbound() (TypedTaggedEventStreamEnvelopeExtmsgUnbound, error) { + var body TypedTaggedEventStreamEnvelopeExtmsgUnbound + err := json.Unmarshal(t.union, &body) + return body, err +} - // LastEventID Reconnect cursor (composite per-city cursor). - LastEventID *string `json:"Last-Event-ID,omitempty"` +// FromTypedTaggedEventStreamEnvelopeExtmsgUnbound overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeExtmsgUnbound +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeExtmsgUnbound(v TypedTaggedEventStreamEnvelopeExtmsgUnbound) error { + v.Type = "extmsg.unbound" + b, err := json.Marshal(v) + t.union = b + return err } -// GetV0ProviderReadinessParams defines parameters for GetV0ProviderReadiness. -type GetV0ProviderReadinessParams struct { - // Providers Comma-separated list of providers to probe. - Providers *string `form:"providers,omitempty" json:"providers,omitempty"` +// MergeTypedTaggedEventStreamEnvelopeExtmsgUnbound performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeExtmsgUnbound +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeExtmsgUnbound(v TypedTaggedEventStreamEnvelopeExtmsgUnbound) error { + v.Type = "extmsg.unbound" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Fresh Force fresh probe, bypassing cache. - Fresh *bool `form:"fresh,omitempty" json:"fresh,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// GetV0ReadinessParams defines parameters for GetV0Readiness. -type GetV0ReadinessParams struct { - // Items Comma-separated list of readiness items to check. - Items *string `form:"items,omitempty" json:"items,omitempty"` +// AsTypedTaggedEventStreamEnvelopeMailArchived returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeMailArchived +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeMailArchived() (TypedTaggedEventStreamEnvelopeMailArchived, error) { + var body TypedTaggedEventStreamEnvelopeMailArchived + err := json.Unmarshal(t.union, &body) + return body, err +} - // Fresh Force fresh probe, bypassing cache. - Fresh *bool `form:"fresh,omitempty" json:"fresh,omitempty"` +// FromTypedTaggedEventStreamEnvelopeMailArchived overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeMailArchived +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeMailArchived(v TypedTaggedEventStreamEnvelopeMailArchived) error { + v.Type = "mail.archived" + b, err := json.Marshal(v) + t.union = b + return err } -// PostV0CityJSONRequestBody defines body for PostV0City for application/json ContentType. -type PostV0CityJSONRequestBody = CityCreateRequest +// MergeTypedTaggedEventStreamEnvelopeMailArchived performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeMailArchived +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeMailArchived(v TypedTaggedEventStreamEnvelopeMailArchived) error { + v.Type = "mail.archived" + b, err := json.Marshal(v) + if err != nil { + return err + } -// PatchV0CityByCityNameJSONRequestBody defines body for PatchV0CityByCityName for application/json ContentType. -type PatchV0CityByCityNameJSONRequestBody = CityPatchInputBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// PatchV0CityByCityNameAgentByBaseJSONRequestBody defines body for PatchV0CityByCityNameAgentByBase for application/json ContentType. -type PatchV0CityByCityNameAgentByBaseJSONRequestBody = AgentUpdateInputBody +// AsTypedTaggedEventStreamEnvelopeMailDeleted returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeMailDeleted +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeMailDeleted() (TypedTaggedEventStreamEnvelopeMailDeleted, error) { + var body TypedTaggedEventStreamEnvelopeMailDeleted + err := json.Unmarshal(t.union, &body) + return body, err +} -// PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody defines body for PatchV0CityByCityNameAgentByDirByBase for application/json ContentType. -type PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody = AgentUpdateQualifiedInputBody +// FromTypedTaggedEventStreamEnvelopeMailDeleted overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeMailDeleted +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeMailDeleted(v TypedTaggedEventStreamEnvelopeMailDeleted) error { + v.Type = "mail.deleted" + b, err := json.Marshal(v) + t.union = b + return err +} -// CreateAgentJSONRequestBody defines body for CreateAgent for application/json ContentType. -type CreateAgentJSONRequestBody = AgentCreateInputBody +// MergeTypedTaggedEventStreamEnvelopeMailDeleted performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeMailDeleted +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeMailDeleted(v TypedTaggedEventStreamEnvelopeMailDeleted) error { + v.Type = "mail.deleted" + b, err := json.Marshal(v) + if err != nil { + return err + } -// PatchV0CityByCityNameBeadByIdJSONRequestBody defines body for PatchV0CityByCityNameBeadById for application/json ContentType. -type PatchV0CityByCityNameBeadByIdJSONRequestBody = BeadUpdateBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// PostV0CityByCityNameBeadByIdAssignJSONRequestBody defines body for PostV0CityByCityNameBeadByIdAssign for application/json ContentType. -type PostV0CityByCityNameBeadByIdAssignJSONRequestBody = BeadAssignInputBody +// AsTypedTaggedEventStreamEnvelopeMailMarkedRead returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeMailMarkedRead +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeMailMarkedRead() (TypedTaggedEventStreamEnvelopeMailMarkedRead, error) { + var body TypedTaggedEventStreamEnvelopeMailMarkedRead + err := json.Unmarshal(t.union, &body) + return body, err +} -// PostV0CityByCityNameBeadByIdUpdateJSONRequestBody defines body for PostV0CityByCityNameBeadByIdUpdate for application/json ContentType. -type PostV0CityByCityNameBeadByIdUpdateJSONRequestBody = BeadUpdateBody +// FromTypedTaggedEventStreamEnvelopeMailMarkedRead overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeMailMarkedRead +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeMailMarkedRead(v TypedTaggedEventStreamEnvelopeMailMarkedRead) error { + v.Type = "mail.marked_read" + b, err := json.Marshal(v) + t.union = b + return err +} -// CreateBeadJSONRequestBody defines body for CreateBead for application/json ContentType. -type CreateBeadJSONRequestBody = BeadCreateInputBody +// MergeTypedTaggedEventStreamEnvelopeMailMarkedRead performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeMailMarkedRead +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeMailMarkedRead(v TypedTaggedEventStreamEnvelopeMailMarkedRead) error { + v.Type = "mail.marked_read" + b, err := json.Marshal(v) + if err != nil { + return err + } -// PostV0CityByCityNameConvoyByIdAddJSONRequestBody defines body for PostV0CityByCityNameConvoyByIdAdd for application/json ContentType. -type PostV0CityByCityNameConvoyByIdAddJSONRequestBody = ConvoyAddInputBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody defines body for PostV0CityByCityNameConvoyByIdRemove for application/json ContentType. -type PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody = ConvoyRemoveInputBody +// AsTypedTaggedEventStreamEnvelopeMailMarkedUnread returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeMailMarkedUnread +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeMailMarkedUnread() (TypedTaggedEventStreamEnvelopeMailMarkedUnread, error) { + var body TypedTaggedEventStreamEnvelopeMailMarkedUnread + err := json.Unmarshal(t.union, &body) + return body, err +} -// CreateConvoyJSONRequestBody defines body for CreateConvoy for application/json ContentType. -type CreateConvoyJSONRequestBody = ConvoyCreateInputBody +// FromTypedTaggedEventStreamEnvelopeMailMarkedUnread overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeMailMarkedUnread +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeMailMarkedUnread(v TypedTaggedEventStreamEnvelopeMailMarkedUnread) error { + v.Type = "mail.marked_unread" + b, err := json.Marshal(v) + t.union = b + return err +} -// EmitEventJSONRequestBody defines body for EmitEvent for application/json ContentType. -type EmitEventJSONRequestBody = EventEmitRequest +// MergeTypedTaggedEventStreamEnvelopeMailMarkedUnread performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeMailMarkedUnread +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeMailMarkedUnread(v TypedTaggedEventStreamEnvelopeMailMarkedUnread) error { + v.Type = "mail.marked_unread" + b, err := json.Marshal(v) + if err != nil { + return err + } -// DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody defines body for DeleteV0CityByCityNameExtmsgAdapters for application/json ContentType. -type DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody = ExtMsgAdapterUnregisterInputBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// RegisterExtmsgAdapterJSONRequestBody defines body for RegisterExtmsgAdapter for application/json ContentType. -type RegisterExtmsgAdapterJSONRequestBody = ExtMsgAdapterRegisterInputBody +// AsTypedTaggedEventStreamEnvelopeMailRead returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeMailRead +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeMailRead() (TypedTaggedEventStreamEnvelopeMailRead, error) { + var body TypedTaggedEventStreamEnvelopeMailRead + err := json.Unmarshal(t.union, &body) + return body, err +} -// PostV0CityByCityNameExtmsgBindJSONRequestBody defines body for PostV0CityByCityNameExtmsgBind for application/json ContentType. -type PostV0CityByCityNameExtmsgBindJSONRequestBody = ExtMsgBindInputBody +// FromTypedTaggedEventStreamEnvelopeMailRead overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeMailRead +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeMailRead(v TypedTaggedEventStreamEnvelopeMailRead) error { + v.Type = "mail.read" + b, err := json.Marshal(v) + t.union = b + return err +} -// EnsureExtmsgGroupJSONRequestBody defines body for EnsureExtmsgGroup for application/json ContentType. -type EnsureExtmsgGroupJSONRequestBody = ExtMsgGroupEnsureInputBody +// MergeTypedTaggedEventStreamEnvelopeMailRead performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeMailRead +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeMailRead(v TypedTaggedEventStreamEnvelopeMailRead) error { + v.Type = "mail.read" + b, err := json.Marshal(v) + if err != nil { + return err + } -// PostV0CityByCityNameExtmsgInboundJSONRequestBody defines body for PostV0CityByCityNameExtmsgInbound for application/json ContentType. -type PostV0CityByCityNameExtmsgInboundJSONRequestBody = ExtMsgInboundInputBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// PostV0CityByCityNameExtmsgOutboundJSONRequestBody defines body for PostV0CityByCityNameExtmsgOutbound for application/json ContentType. -type PostV0CityByCityNameExtmsgOutboundJSONRequestBody = ExtMsgOutboundInputBody +// AsTypedTaggedEventStreamEnvelopeMailReplied returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeMailReplied +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeMailReplied() (TypedTaggedEventStreamEnvelopeMailReplied, error) { + var body TypedTaggedEventStreamEnvelopeMailReplied + err := json.Unmarshal(t.union, &body) + return body, err +} -// DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody defines body for DeleteV0CityByCityNameExtmsgParticipants for application/json ContentType. -type DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody = ExtMsgParticipantRemoveInputBody +// FromTypedTaggedEventStreamEnvelopeMailReplied overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeMailReplied +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeMailReplied(v TypedTaggedEventStreamEnvelopeMailReplied) error { + v.Type = "mail.replied" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTypedTaggedEventStreamEnvelopeMailReplied performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeMailReplied +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeMailReplied(v TypedTaggedEventStreamEnvelopeMailReplied) error { + v.Type = "mail.replied" + b, err := json.Marshal(v) + if err != nil { + return err + } -// PostV0CityByCityNameExtmsgParticipantsJSONRequestBody defines body for PostV0CityByCityNameExtmsgParticipants for application/json ContentType. -type PostV0CityByCityNameExtmsgParticipantsJSONRequestBody = ExtMsgParticipantUpsertInputBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody defines body for PostV0CityByCityNameExtmsgTranscriptAck for application/json ContentType. -type PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody = ExtMsgTranscriptAckInputBody +// AsTypedTaggedEventStreamEnvelopeMailSent returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeMailSent +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeMailSent() (TypedTaggedEventStreamEnvelopeMailSent, error) { + var body TypedTaggedEventStreamEnvelopeMailSent + err := json.Unmarshal(t.union, &body) + return body, err +} -// PostV0CityByCityNameExtmsgUnbindJSONRequestBody defines body for PostV0CityByCityNameExtmsgUnbind for application/json ContentType. -type PostV0CityByCityNameExtmsgUnbindJSONRequestBody = ExtMsgUnbindInputBody +// FromTypedTaggedEventStreamEnvelopeMailSent overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeMailSent +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeMailSent(v TypedTaggedEventStreamEnvelopeMailSent) error { + v.Type = "mail.sent" + b, err := json.Marshal(v) + t.union = b + return err +} -// PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody defines body for PostV0CityByCityNameFormulasByNamePreview for application/json ContentType. -type PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody = FormulaPreviewBody +// MergeTypedTaggedEventStreamEnvelopeMailSent performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeMailSent +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeMailSent(v TypedTaggedEventStreamEnvelopeMailSent) error { + v.Type = "mail.sent" + b, err := json.Marshal(v) + if err != nil { + return err + } -// SendMailJSONRequestBody defines body for SendMail for application/json ContentType. -type SendMailJSONRequestBody = MailSendInputBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// ReplyMailJSONRequestBody defines body for ReplyMail for application/json ContentType. -type ReplyMailJSONRequestBody = MailReplyInputBody +// AsTypedTaggedEventStreamEnvelopeOrderCompleted returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeOrderCompleted +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeOrderCompleted() (TypedTaggedEventStreamEnvelopeOrderCompleted, error) { + var body TypedTaggedEventStreamEnvelopeOrderCompleted + err := json.Unmarshal(t.union, &body) + return body, err +} -// PutV0CityByCityNamePatchesAgentsJSONRequestBody defines body for PutV0CityByCityNamePatchesAgents for application/json ContentType. -type PutV0CityByCityNamePatchesAgentsJSONRequestBody = AgentPatchSetInputBody +// FromTypedTaggedEventStreamEnvelopeOrderCompleted overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeOrderCompleted +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeOrderCompleted(v TypedTaggedEventStreamEnvelopeOrderCompleted) error { + v.Type = "order.completed" + b, err := json.Marshal(v) + t.union = b + return err +} -// PutV0CityByCityNamePatchesProvidersJSONRequestBody defines body for PutV0CityByCityNamePatchesProviders for application/json ContentType. -type PutV0CityByCityNamePatchesProvidersJSONRequestBody = ProviderPatchSetInputBody +// MergeTypedTaggedEventStreamEnvelopeOrderCompleted performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeOrderCompleted +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeOrderCompleted(v TypedTaggedEventStreamEnvelopeOrderCompleted) error { + v.Type = "order.completed" + b, err := json.Marshal(v) + if err != nil { + return err + } -// PutV0CityByCityNamePatchesRigsJSONRequestBody defines body for PutV0CityByCityNamePatchesRigs for application/json ContentType. -type PutV0CityByCityNamePatchesRigsJSONRequestBody = RigPatchSetInputBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// PatchV0CityByCityNameProviderByNameJSONRequestBody defines body for PatchV0CityByCityNameProviderByName for application/json ContentType. -type PatchV0CityByCityNameProviderByNameJSONRequestBody = ProviderUpdateInputBody +// AsTypedTaggedEventStreamEnvelopeOrderFailed returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeOrderFailed +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeOrderFailed() (TypedTaggedEventStreamEnvelopeOrderFailed, error) { + var body TypedTaggedEventStreamEnvelopeOrderFailed + err := json.Unmarshal(t.union, &body) + return body, err +} -// CreateProviderJSONRequestBody defines body for CreateProvider for application/json ContentType. -type CreateProviderJSONRequestBody = ProviderCreateInputBody +// FromTypedTaggedEventStreamEnvelopeOrderFailed overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeOrderFailed +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeOrderFailed(v TypedTaggedEventStreamEnvelopeOrderFailed) error { + v.Type = "order.failed" + b, err := json.Marshal(v) + t.union = b + return err +} -// PatchV0CityByCityNameRigByNameJSONRequestBody defines body for PatchV0CityByCityNameRigByName for application/json ContentType. -type PatchV0CityByCityNameRigByNameJSONRequestBody = RigUpdateInputBody +// MergeTypedTaggedEventStreamEnvelopeOrderFailed performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeOrderFailed +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeOrderFailed(v TypedTaggedEventStreamEnvelopeOrderFailed) error { + v.Type = "order.failed" + b, err := json.Marshal(v) + if err != nil { + return err + } -// CreateRigJSONRequestBody defines body for CreateRig for application/json ContentType. -type CreateRigJSONRequestBody = RigCreateInputBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// PatchV0CityByCityNameSessionByIdJSONRequestBody defines body for PatchV0CityByCityNameSessionById for application/json ContentType. -type PatchV0CityByCityNameSessionByIdJSONRequestBody = SessionPatchBody +// AsTypedTaggedEventStreamEnvelopeOrderFired returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeOrderFired +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeOrderFired() (TypedTaggedEventStreamEnvelopeOrderFired, error) { + var body TypedTaggedEventStreamEnvelopeOrderFired + err := json.Unmarshal(t.union, &body) + return body, err +} -// SendSessionMessageJSONRequestBody defines body for SendSessionMessage for application/json ContentType. -type SendSessionMessageJSONRequestBody = SessionMessageInputBody +// FromTypedTaggedEventStreamEnvelopeOrderFired overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeOrderFired +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeOrderFired(v TypedTaggedEventStreamEnvelopeOrderFired) error { + v.Type = "order.fired" + b, err := json.Marshal(v) + t.union = b + return err +} -// PostV0CityByCityNameSessionByIdRenameJSONRequestBody defines body for PostV0CityByCityNameSessionByIdRename for application/json ContentType. -type PostV0CityByCityNameSessionByIdRenameJSONRequestBody = SessionRenameInputBody +// MergeTypedTaggedEventStreamEnvelopeOrderFired performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeOrderFired +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeOrderFired(v TypedTaggedEventStreamEnvelopeOrderFired) error { + v.Type = "order.fired" + b, err := json.Marshal(v) + if err != nil { + return err + } -// RespondSessionJSONRequestBody defines body for RespondSession for application/json ContentType. -type RespondSessionJSONRequestBody = SessionRespondInputBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// SubmitSessionJSONRequestBody defines body for SubmitSession for application/json ContentType. -type SubmitSessionJSONRequestBody = SessionSubmitInputBody +// AsTypedTaggedEventStreamEnvelopeProviderSwapped returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeProviderSwapped +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeProviderSwapped() (TypedTaggedEventStreamEnvelopeProviderSwapped, error) { + var body TypedTaggedEventStreamEnvelopeProviderSwapped + err := json.Unmarshal(t.union, &body) + return body, err +} -// CreateSessionJSONRequestBody defines body for CreateSession for application/json ContentType. -type CreateSessionJSONRequestBody = SessionCreateBody +// FromTypedTaggedEventStreamEnvelopeProviderSwapped overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeProviderSwapped +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeProviderSwapped(v TypedTaggedEventStreamEnvelopeProviderSwapped) error { + v.Type = "provider.swapped" + b, err := json.Marshal(v) + t.union = b + return err +} -// PostV0CityByCityNameSlingJSONRequestBody defines body for PostV0CityByCityNameSling for application/json ContentType. -type PostV0CityByCityNameSlingJSONRequestBody = SlingInputBody +// MergeTypedTaggedEventStreamEnvelopeProviderSwapped performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeProviderSwapped +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeProviderSwapped(v TypedTaggedEventStreamEnvelopeProviderSwapped) error { + v.Type = "provider.swapped" + b, err := json.Marshal(v) + if err != nil { + return err + } -// AsAdapterEventPayload returns the union data inside the EventPayload as a AdapterEventPayload -func (t EventPayload) AsAdapterEventPayload() (AdapterEventPayload, error) { - var body AdapterEventPayload + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTypedTaggedEventStreamEnvelopeSessionCrashed returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeSessionCrashed +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeSessionCrashed() (TypedTaggedEventStreamEnvelopeSessionCrashed, error) { + var body TypedTaggedEventStreamEnvelopeSessionCrashed err := json.Unmarshal(t.union, &body) return body, err } -// FromAdapterEventPayload overwrites any union data inside the EventPayload as the provided AdapterEventPayload -func (t *EventPayload) FromAdapterEventPayload(v AdapterEventPayload) error { +// FromTypedTaggedEventStreamEnvelopeSessionCrashed overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeSessionCrashed +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeSessionCrashed(v TypedTaggedEventStreamEnvelopeSessionCrashed) error { + v.Type = "session.crashed" b, err := json.Marshal(v) t.union = b return err } -// MergeAdapterEventPayload performs a merge with any union data inside the EventPayload, using the provided AdapterEventPayload -func (t *EventPayload) MergeAdapterEventPayload(v AdapterEventPayload) error { +// MergeTypedTaggedEventStreamEnvelopeSessionCrashed performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeSessionCrashed +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeSessionCrashed(v TypedTaggedEventStreamEnvelopeSessionCrashed) error { + v.Type = "session.crashed" b, err := json.Marshal(v) if err != nil { return err @@ -3276,22 +7450,24 @@ func (t *EventPayload) MergeAdapterEventPayload(v AdapterEventPayload) error { return err } -// AsBeadEventPayload returns the union data inside the EventPayload as a BeadEventPayload -func (t EventPayload) AsBeadEventPayload() (BeadEventPayload, error) { - var body BeadEventPayload +// AsTypedTaggedEventStreamEnvelopeSessionDraining returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeSessionDraining +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeSessionDraining() (TypedTaggedEventStreamEnvelopeSessionDraining, error) { + var body TypedTaggedEventStreamEnvelopeSessionDraining err := json.Unmarshal(t.union, &body) return body, err } -// FromBeadEventPayload overwrites any union data inside the EventPayload as the provided BeadEventPayload -func (t *EventPayload) FromBeadEventPayload(v BeadEventPayload) error { +// FromTypedTaggedEventStreamEnvelopeSessionDraining overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeSessionDraining +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeSessionDraining(v TypedTaggedEventStreamEnvelopeSessionDraining) error { + v.Type = "session.draining" b, err := json.Marshal(v) t.union = b return err } -// MergeBeadEventPayload performs a merge with any union data inside the EventPayload, using the provided BeadEventPayload -func (t *EventPayload) MergeBeadEventPayload(v BeadEventPayload) error { +// MergeTypedTaggedEventStreamEnvelopeSessionDraining performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeSessionDraining +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeSessionDraining(v TypedTaggedEventStreamEnvelopeSessionDraining) error { + v.Type = "session.draining" b, err := json.Marshal(v) if err != nil { return err @@ -3302,22 +7478,24 @@ func (t *EventPayload) MergeBeadEventPayload(v BeadEventPayload) error { return err } -// AsBoundEventPayload returns the union data inside the EventPayload as a BoundEventPayload -func (t EventPayload) AsBoundEventPayload() (BoundEventPayload, error) { - var body BoundEventPayload +// AsTypedTaggedEventStreamEnvelopeSessionIdleKilled returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeSessionIdleKilled +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeSessionIdleKilled() (TypedTaggedEventStreamEnvelopeSessionIdleKilled, error) { + var body TypedTaggedEventStreamEnvelopeSessionIdleKilled err := json.Unmarshal(t.union, &body) return body, err } -// FromBoundEventPayload overwrites any union data inside the EventPayload as the provided BoundEventPayload -func (t *EventPayload) FromBoundEventPayload(v BoundEventPayload) error { +// FromTypedTaggedEventStreamEnvelopeSessionIdleKilled overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeSessionIdleKilled +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeSessionIdleKilled(v TypedTaggedEventStreamEnvelopeSessionIdleKilled) error { + v.Type = "session.idle_killed" b, err := json.Marshal(v) t.union = b return err } -// MergeBoundEventPayload performs a merge with any union data inside the EventPayload, using the provided BoundEventPayload -func (t *EventPayload) MergeBoundEventPayload(v BoundEventPayload) error { +// MergeTypedTaggedEventStreamEnvelopeSessionIdleKilled performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeSessionIdleKilled +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeSessionIdleKilled(v TypedTaggedEventStreamEnvelopeSessionIdleKilled) error { + v.Type = "session.idle_killed" b, err := json.Marshal(v) if err != nil { return err @@ -3328,22 +7506,24 @@ func (t *EventPayload) MergeBoundEventPayload(v BoundEventPayload) error { return err } -// AsGroupCreatedEventPayload returns the union data inside the EventPayload as a GroupCreatedEventPayload -func (t EventPayload) AsGroupCreatedEventPayload() (GroupCreatedEventPayload, error) { - var body GroupCreatedEventPayload +// AsTypedTaggedEventStreamEnvelopeSessionQuarantined returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeSessionQuarantined +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeSessionQuarantined() (TypedTaggedEventStreamEnvelopeSessionQuarantined, error) { + var body TypedTaggedEventStreamEnvelopeSessionQuarantined err := json.Unmarshal(t.union, &body) return body, err } -// FromGroupCreatedEventPayload overwrites any union data inside the EventPayload as the provided GroupCreatedEventPayload -func (t *EventPayload) FromGroupCreatedEventPayload(v GroupCreatedEventPayload) error { +// FromTypedTaggedEventStreamEnvelopeSessionQuarantined overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeSessionQuarantined +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeSessionQuarantined(v TypedTaggedEventStreamEnvelopeSessionQuarantined) error { + v.Type = "session.quarantined" b, err := json.Marshal(v) t.union = b return err } -// MergeGroupCreatedEventPayload performs a merge with any union data inside the EventPayload, using the provided GroupCreatedEventPayload -func (t *EventPayload) MergeGroupCreatedEventPayload(v GroupCreatedEventPayload) error { +// MergeTypedTaggedEventStreamEnvelopeSessionQuarantined performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeSessionQuarantined +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeSessionQuarantined(v TypedTaggedEventStreamEnvelopeSessionQuarantined) error { + v.Type = "session.quarantined" b, err := json.Marshal(v) if err != nil { return err @@ -3354,22 +7534,24 @@ func (t *EventPayload) MergeGroupCreatedEventPayload(v GroupCreatedEventPayload) return err } -// AsInboundEventPayload returns the union data inside the EventPayload as a InboundEventPayload -func (t EventPayload) AsInboundEventPayload() (InboundEventPayload, error) { - var body InboundEventPayload +// AsTypedTaggedEventStreamEnvelopeSessionStopped returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeSessionStopped +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeSessionStopped() (TypedTaggedEventStreamEnvelopeSessionStopped, error) { + var body TypedTaggedEventStreamEnvelopeSessionStopped err := json.Unmarshal(t.union, &body) return body, err } -// FromInboundEventPayload overwrites any union data inside the EventPayload as the provided InboundEventPayload -func (t *EventPayload) FromInboundEventPayload(v InboundEventPayload) error { +// FromTypedTaggedEventStreamEnvelopeSessionStopped overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeSessionStopped +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeSessionStopped(v TypedTaggedEventStreamEnvelopeSessionStopped) error { + v.Type = "session.stopped" b, err := json.Marshal(v) t.union = b return err } -// MergeInboundEventPayload performs a merge with any union data inside the EventPayload, using the provided InboundEventPayload -func (t *EventPayload) MergeInboundEventPayload(v InboundEventPayload) error { +// MergeTypedTaggedEventStreamEnvelopeSessionStopped performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeSessionStopped +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeSessionStopped(v TypedTaggedEventStreamEnvelopeSessionStopped) error { + v.Type = "session.stopped" b, err := json.Marshal(v) if err != nil { return err @@ -3380,22 +7562,24 @@ func (t *EventPayload) MergeInboundEventPayload(v InboundEventPayload) error { return err } -// AsMailEventPayload returns the union data inside the EventPayload as a MailEventPayload -func (t EventPayload) AsMailEventPayload() (MailEventPayload, error) { - var body MailEventPayload +// AsTypedTaggedEventStreamEnvelopeSessionSuspended returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeSessionSuspended +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeSessionSuspended() (TypedTaggedEventStreamEnvelopeSessionSuspended, error) { + var body TypedTaggedEventStreamEnvelopeSessionSuspended err := json.Unmarshal(t.union, &body) return body, err } -// FromMailEventPayload overwrites any union data inside the EventPayload as the provided MailEventPayload -func (t *EventPayload) FromMailEventPayload(v MailEventPayload) error { +// FromTypedTaggedEventStreamEnvelopeSessionSuspended overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeSessionSuspended +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeSessionSuspended(v TypedTaggedEventStreamEnvelopeSessionSuspended) error { + v.Type = "session.suspended" b, err := json.Marshal(v) t.union = b return err } -// MergeMailEventPayload performs a merge with any union data inside the EventPayload, using the provided MailEventPayload -func (t *EventPayload) MergeMailEventPayload(v MailEventPayload) error { +// MergeTypedTaggedEventStreamEnvelopeSessionSuspended performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeSessionSuspended +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeSessionSuspended(v TypedTaggedEventStreamEnvelopeSessionSuspended) error { + v.Type = "session.suspended" b, err := json.Marshal(v) if err != nil { return err @@ -3406,22 +7590,24 @@ func (t *EventPayload) MergeMailEventPayload(v MailEventPayload) error { return err } -// AsNoPayload returns the union data inside the EventPayload as a NoPayload -func (t EventPayload) AsNoPayload() (NoPayload, error) { - var body NoPayload +// AsTypedTaggedEventStreamEnvelopeSessionUndrained returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeSessionUndrained +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeSessionUndrained() (TypedTaggedEventStreamEnvelopeSessionUndrained, error) { + var body TypedTaggedEventStreamEnvelopeSessionUndrained err := json.Unmarshal(t.union, &body) return body, err } -// FromNoPayload overwrites any union data inside the EventPayload as the provided NoPayload -func (t *EventPayload) FromNoPayload(v NoPayload) error { +// FromTypedTaggedEventStreamEnvelopeSessionUndrained overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeSessionUndrained +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeSessionUndrained(v TypedTaggedEventStreamEnvelopeSessionUndrained) error { + v.Type = "session.undrained" b, err := json.Marshal(v) t.union = b return err } -// MergeNoPayload performs a merge with any union data inside the EventPayload, using the provided NoPayload -func (t *EventPayload) MergeNoPayload(v NoPayload) error { +// MergeTypedTaggedEventStreamEnvelopeSessionUndrained performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeSessionUndrained +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeSessionUndrained(v TypedTaggedEventStreamEnvelopeSessionUndrained) error { + v.Type = "session.undrained" b, err := json.Marshal(v) if err != nil { return err @@ -3432,22 +7618,24 @@ func (t *EventPayload) MergeNoPayload(v NoPayload) error { return err } -// AsOutboundEventPayload returns the union data inside the EventPayload as a OutboundEventPayload -func (t EventPayload) AsOutboundEventPayload() (OutboundEventPayload, error) { - var body OutboundEventPayload +// AsTypedTaggedEventStreamEnvelopeSessionUpdated returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeSessionUpdated +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeSessionUpdated() (TypedTaggedEventStreamEnvelopeSessionUpdated, error) { + var body TypedTaggedEventStreamEnvelopeSessionUpdated err := json.Unmarshal(t.union, &body) return body, err } -// FromOutboundEventPayload overwrites any union data inside the EventPayload as the provided OutboundEventPayload -func (t *EventPayload) FromOutboundEventPayload(v OutboundEventPayload) error { +// FromTypedTaggedEventStreamEnvelopeSessionUpdated overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeSessionUpdated +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeSessionUpdated(v TypedTaggedEventStreamEnvelopeSessionUpdated) error { + v.Type = "session.updated" b, err := json.Marshal(v) t.union = b return err } -// MergeOutboundEventPayload performs a merge with any union data inside the EventPayload, using the provided OutboundEventPayload -func (t *EventPayload) MergeOutboundEventPayload(v OutboundEventPayload) error { +// MergeTypedTaggedEventStreamEnvelopeSessionUpdated performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeSessionUpdated +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeSessionUpdated(v TypedTaggedEventStreamEnvelopeSessionUpdated) error { + v.Type = "session.updated" b, err := json.Marshal(v) if err != nil { return err @@ -3458,22 +7646,24 @@ func (t *EventPayload) MergeOutboundEventPayload(v OutboundEventPayload) error { return err } -// AsUnboundEventPayload returns the union data inside the EventPayload as a UnboundEventPayload -func (t EventPayload) AsUnboundEventPayload() (UnboundEventPayload, error) { - var body UnboundEventPayload +// AsTypedTaggedEventStreamEnvelopeSessionWoke returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeSessionWoke +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeSessionWoke() (TypedTaggedEventStreamEnvelopeSessionWoke, error) { + var body TypedTaggedEventStreamEnvelopeSessionWoke err := json.Unmarshal(t.union, &body) return body, err } -// FromUnboundEventPayload overwrites any union data inside the EventPayload as the provided UnboundEventPayload -func (t *EventPayload) FromUnboundEventPayload(v UnboundEventPayload) error { +// FromTypedTaggedEventStreamEnvelopeSessionWoke overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeSessionWoke +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeSessionWoke(v TypedTaggedEventStreamEnvelopeSessionWoke) error { + v.Type = "session.woke" b, err := json.Marshal(v) t.union = b return err } -// MergeUnboundEventPayload performs a merge with any union data inside the EventPayload, using the provided UnboundEventPayload -func (t *EventPayload) MergeUnboundEventPayload(v UnboundEventPayload) error { +// MergeTypedTaggedEventStreamEnvelopeSessionWoke performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeSessionWoke +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeSessionWoke(v TypedTaggedEventStreamEnvelopeSessionWoke) error { + v.Type = "session.woke" b, err := json.Marshal(v) if err != nil { return err @@ -3484,22 +7674,24 @@ func (t *EventPayload) MergeUnboundEventPayload(v UnboundEventPayload) error { return err } -// AsWorkerOperationEventPayload returns the union data inside the EventPayload as a WorkerOperationEventPayload -func (t EventPayload) AsWorkerOperationEventPayload() (WorkerOperationEventPayload, error) { - var body WorkerOperationEventPayload +// AsTypedTaggedEventStreamEnvelopeWorkerOperation returns the union data inside the TypedTaggedEventStreamEnvelope as a TypedTaggedEventStreamEnvelopeWorkerOperation +func (t TypedTaggedEventStreamEnvelope) AsTypedTaggedEventStreamEnvelopeWorkerOperation() (TypedTaggedEventStreamEnvelopeWorkerOperation, error) { + var body TypedTaggedEventStreamEnvelopeWorkerOperation err := json.Unmarshal(t.union, &body) return body, err } -// FromWorkerOperationEventPayload overwrites any union data inside the EventPayload as the provided WorkerOperationEventPayload -func (t *EventPayload) FromWorkerOperationEventPayload(v WorkerOperationEventPayload) error { +// FromTypedTaggedEventStreamEnvelopeWorkerOperation overwrites any union data inside the TypedTaggedEventStreamEnvelope as the provided TypedTaggedEventStreamEnvelopeWorkerOperation +func (t *TypedTaggedEventStreamEnvelope) FromTypedTaggedEventStreamEnvelopeWorkerOperation(v TypedTaggedEventStreamEnvelopeWorkerOperation) error { + v.Type = "worker.operation" b, err := json.Marshal(v) t.union = b return err } -// MergeWorkerOperationEventPayload performs a merge with any union data inside the EventPayload, using the provided WorkerOperationEventPayload -func (t *EventPayload) MergeWorkerOperationEventPayload(v WorkerOperationEventPayload) error { +// MergeTypedTaggedEventStreamEnvelopeWorkerOperation performs a merge with any union data inside the TypedTaggedEventStreamEnvelope, using the provided TypedTaggedEventStreamEnvelopeWorkerOperation +func (t *TypedTaggedEventStreamEnvelope) MergeTypedTaggedEventStreamEnvelopeWorkerOperation(v TypedTaggedEventStreamEnvelopeWorkerOperation) error { + v.Type = "worker.operation" b, err := json.Marshal(v) if err != nil { return err @@ -3510,12 +7702,117 @@ func (t *EventPayload) MergeWorkerOperationEventPayload(v WorkerOperationEventPa return err } -func (t EventPayload) MarshalJSON() ([]byte, error) { +func (t TypedTaggedEventStreamEnvelope) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"type"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t TypedTaggedEventStreamEnvelope) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "bead.closed": + return t.AsTypedTaggedEventStreamEnvelopeBeadClosed() + case "bead.created": + return t.AsTypedTaggedEventStreamEnvelopeBeadCreated() + case "bead.updated": + return t.AsTypedTaggedEventStreamEnvelopeBeadUpdated() + case "city.created": + return t.AsTypedTaggedEventStreamEnvelopeCityCreated() + case "city.init_failed": + return t.AsTypedTaggedEventStreamEnvelopeCityInitFailed() + case "city.ready": + return t.AsTypedTaggedEventStreamEnvelopeCityReady() + case "city.resumed": + return t.AsTypedTaggedEventStreamEnvelopeCityResumed() + case "city.suspended": + return t.AsTypedTaggedEventStreamEnvelopeCitySuspended() + case "city.unregister_failed": + return t.AsTypedTaggedEventStreamEnvelopeCityUnregisterFailed() + case "city.unregister_requested": + return t.AsTypedTaggedEventStreamEnvelopeCityUnregisterRequested() + case "city.unregistered": + return t.AsTypedTaggedEventStreamEnvelopeCityUnregistered() + case "controller.started": + return t.AsTypedTaggedEventStreamEnvelopeControllerStarted() + case "controller.stopped": + return t.AsTypedTaggedEventStreamEnvelopeControllerStopped() + case "convoy.closed": + return t.AsTypedTaggedEventStreamEnvelopeConvoyClosed() + case "convoy.created": + return t.AsTypedTaggedEventStreamEnvelopeConvoyCreated() + case "extmsg.adapter_added": + return t.AsTypedTaggedEventStreamEnvelopeExtmsgAdapterAdded() + case "extmsg.adapter_removed": + return t.AsTypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved() + case "extmsg.bound": + return t.AsTypedTaggedEventStreamEnvelopeExtmsgBound() + case "extmsg.group_created": + return t.AsTypedTaggedEventStreamEnvelopeExtmsgGroupCreated() + case "extmsg.inbound": + return t.AsTypedTaggedEventStreamEnvelopeExtmsgInbound() + case "extmsg.outbound": + return t.AsTypedTaggedEventStreamEnvelopeExtmsgOutbound() + case "extmsg.unbound": + return t.AsTypedTaggedEventStreamEnvelopeExtmsgUnbound() + case "mail.archived": + return t.AsTypedTaggedEventStreamEnvelopeMailArchived() + case "mail.deleted": + return t.AsTypedTaggedEventStreamEnvelopeMailDeleted() + case "mail.marked_read": + return t.AsTypedTaggedEventStreamEnvelopeMailMarkedRead() + case "mail.marked_unread": + return t.AsTypedTaggedEventStreamEnvelopeMailMarkedUnread() + case "mail.read": + return t.AsTypedTaggedEventStreamEnvelopeMailRead() + case "mail.replied": + return t.AsTypedTaggedEventStreamEnvelopeMailReplied() + case "mail.sent": + return t.AsTypedTaggedEventStreamEnvelopeMailSent() + case "order.completed": + return t.AsTypedTaggedEventStreamEnvelopeOrderCompleted() + case "order.failed": + return t.AsTypedTaggedEventStreamEnvelopeOrderFailed() + case "order.fired": + return t.AsTypedTaggedEventStreamEnvelopeOrderFired() + case "provider.swapped": + return t.AsTypedTaggedEventStreamEnvelopeProviderSwapped() + case "session.crashed": + return t.AsTypedTaggedEventStreamEnvelopeSessionCrashed() + case "session.draining": + return t.AsTypedTaggedEventStreamEnvelopeSessionDraining() + case "session.idle_killed": + return t.AsTypedTaggedEventStreamEnvelopeSessionIdleKilled() + case "session.quarantined": + return t.AsTypedTaggedEventStreamEnvelopeSessionQuarantined() + case "session.stopped": + return t.AsTypedTaggedEventStreamEnvelopeSessionStopped() + case "session.suspended": + return t.AsTypedTaggedEventStreamEnvelopeSessionSuspended() + case "session.undrained": + return t.AsTypedTaggedEventStreamEnvelopeSessionUndrained() + case "session.updated": + return t.AsTypedTaggedEventStreamEnvelopeSessionUpdated() + case "session.woke": + return t.AsTypedTaggedEventStreamEnvelopeSessionWoke() + case "worker.operation": + return t.AsTypedTaggedEventStreamEnvelopeWorkerOperation() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} + +func (t TypedTaggedEventStreamEnvelope) MarshalJSON() ([]byte, error) { b, err := t.union.MarshalJSON() return b, err } -func (t *EventPayload) UnmarshalJSON(b []byte) error { +func (t *TypedTaggedEventStreamEnvelope) UnmarshalJSON(b []byte) error { err := t.union.UnmarshalJSON(b) return err } @@ -3600,28 +7897,28 @@ type ClientInterface interface { GetV0Cities(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityWithBody request with any body - PostV0CityWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityWithBody(ctx context.Context, params *PostV0CityParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0City(ctx context.Context, body PostV0CityJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0City(ctx context.Context, params *PostV0CityParams, body PostV0CityJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityName request GetV0CityByCityName(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*http.Response, error) // PatchV0CityByCityNameWithBody request with any body - PatchV0CityByCityNameWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameWithBody(ctx context.Context, cityName string, params *PatchV0CityByCityNameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PatchV0CityByCityName(ctx context.Context, cityName string, body PatchV0CityByCityNameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityName(ctx context.Context, cityName string, params *PatchV0CityByCityNameParams, body PatchV0CityByCityNameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // DeleteV0CityByCityNameAgentByBase request - DeleteV0CityByCityNameAgentByBase(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNameAgentByBase(ctx context.Context, cityName string, base string, params *DeleteV0CityByCityNameAgentByBaseParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameAgentByBase request GetV0CityByCityNameAgentByBase(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) // PatchV0CityByCityNameAgentByBaseWithBody request with any body - PatchV0CityByCityNameAgentByBaseWithBody(ctx context.Context, cityName string, base string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameAgentByBaseWithBody(ctx context.Context, cityName string, base string, params *PatchV0CityByCityNameAgentByBaseParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PatchV0CityByCityNameAgentByBase(ctx context.Context, cityName string, base string, body PatchV0CityByCityNameAgentByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameAgentByBase(ctx context.Context, cityName string, base string, params *PatchV0CityByCityNameAgentByBaseParams, body PatchV0CityByCityNameAgentByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameAgentByBaseOutput request GetV0CityByCityNameAgentByBaseOutput(ctx context.Context, cityName string, base string, params *GetV0CityByCityNameAgentByBaseOutputParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3630,18 +7927,18 @@ type ClientInterface interface { StreamAgentOutput(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameAgentByBaseByAction request - PostV0CityByCityNameAgentByBaseByAction(ctx context.Context, cityName string, base string, action PostV0CityByCityNameAgentByBaseByActionParamsAction, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameAgentByBaseByAction(ctx context.Context, cityName string, base string, action PostV0CityByCityNameAgentByBaseByActionParamsAction, params *PostV0CityByCityNameAgentByBaseByActionParams, reqEditors ...RequestEditorFn) (*http.Response, error) // DeleteV0CityByCityNameAgentByDirByBase request - DeleteV0CityByCityNameAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNameAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, params *DeleteV0CityByCityNameAgentByDirByBaseParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameAgentByDirByBase request GetV0CityByCityNameAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) // PatchV0CityByCityNameAgentByDirByBaseWithBody request with any body - PatchV0CityByCityNameAgentByDirByBaseWithBody(ctx context.Context, cityName string, dir string, base string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameAgentByDirByBaseWithBody(ctx context.Context, cityName string, dir string, base string, params *PatchV0CityByCityNameAgentByDirByBaseParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PatchV0CityByCityNameAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, body PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, params *PatchV0CityByCityNameAgentByDirByBaseParams, body PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameAgentByDirByBaseOutput request GetV0CityByCityNameAgentByDirByBaseOutput(ctx context.Context, cityName string, dir string, base string, params *GetV0CityByCityNameAgentByDirByBaseOutputParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3650,45 +7947,45 @@ type ClientInterface interface { StreamAgentOutputQualified(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameAgentByDirByBaseByAction request - PostV0CityByCityNameAgentByDirByBaseByAction(ctx context.Context, cityName string, dir string, base string, action PostV0CityByCityNameAgentByDirByBaseByActionParamsAction, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameAgentByDirByBaseByAction(ctx context.Context, cityName string, dir string, base string, action PostV0CityByCityNameAgentByDirByBaseByActionParamsAction, params *PostV0CityByCityNameAgentByDirByBaseByActionParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameAgents request GetV0CityByCityNameAgents(ctx context.Context, cityName string, params *GetV0CityByCityNameAgentsParams, reqEditors ...RequestEditorFn) (*http.Response, error) // CreateAgentWithBody request with any body - CreateAgentWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + CreateAgentWithBody(ctx context.Context, cityName string, params *CreateAgentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - CreateAgent(ctx context.Context, cityName string, body CreateAgentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + CreateAgent(ctx context.Context, cityName string, params *CreateAgentParams, body CreateAgentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // DeleteV0CityByCityNameBeadById request - DeleteV0CityByCityNameBeadById(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNameBeadById(ctx context.Context, cityName string, id string, params *DeleteV0CityByCityNameBeadByIdParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameBeadById request GetV0CityByCityNameBeadById(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) // PatchV0CityByCityNameBeadByIdWithBody request with any body - PatchV0CityByCityNameBeadByIdWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameBeadByIdWithBody(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameBeadByIdParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PatchV0CityByCityNameBeadById(ctx context.Context, cityName string, id string, body PatchV0CityByCityNameBeadByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameBeadById(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameBeadByIdParams, body PatchV0CityByCityNameBeadByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameBeadByIdAssignWithBody request with any body - PostV0CityByCityNameBeadByIdAssignWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameBeadByIdAssignWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdAssignParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameBeadByIdAssign(ctx context.Context, cityName string, id string, body PostV0CityByCityNameBeadByIdAssignJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameBeadByIdAssign(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdAssignParams, body PostV0CityByCityNameBeadByIdAssignJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameBeadByIdClose request - PostV0CityByCityNameBeadByIdClose(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameBeadByIdClose(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdCloseParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameBeadByIdDeps request GetV0CityByCityNameBeadByIdDeps(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameBeadByIdReopen request - PostV0CityByCityNameBeadByIdReopen(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameBeadByIdReopen(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdReopenParams, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameBeadByIdUpdateWithBody request with any body - PostV0CityByCityNameBeadByIdUpdateWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameBeadByIdUpdateWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdUpdateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameBeadByIdUpdate(ctx context.Context, cityName string, id string, body PostV0CityByCityNameBeadByIdUpdateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameBeadByIdUpdate(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdUpdateParams, body PostV0CityByCityNameBeadByIdUpdateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameBeads request GetV0CityByCityNameBeads(ctx context.Context, cityName string, params *GetV0CityByCityNameBeadsParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3714,63 +8011,63 @@ type ClientInterface interface { GetV0CityByCityNameConfigValidate(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*http.Response, error) // DeleteV0CityByCityNameConvoyById request - DeleteV0CityByCityNameConvoyById(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNameConvoyById(ctx context.Context, cityName string, id string, params *DeleteV0CityByCityNameConvoyByIdParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameConvoyById request GetV0CityByCityNameConvoyById(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameConvoyByIdAddWithBody request with any body - PostV0CityByCityNameConvoyByIdAddWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameConvoyByIdAddWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdAddParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameConvoyByIdAdd(ctx context.Context, cityName string, id string, body PostV0CityByCityNameConvoyByIdAddJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameConvoyByIdAdd(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdAddParams, body PostV0CityByCityNameConvoyByIdAddJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameConvoyByIdCheck request GetV0CityByCityNameConvoyByIdCheck(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameConvoyByIdClose request - PostV0CityByCityNameConvoyByIdClose(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameConvoyByIdClose(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdCloseParams, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameConvoyByIdRemoveWithBody request with any body - PostV0CityByCityNameConvoyByIdRemoveWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameConvoyByIdRemoveWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdRemoveParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameConvoyByIdRemove(ctx context.Context, cityName string, id string, body PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameConvoyByIdRemove(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdRemoveParams, body PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameConvoys request GetV0CityByCityNameConvoys(ctx context.Context, cityName string, params *GetV0CityByCityNameConvoysParams, reqEditors ...RequestEditorFn) (*http.Response, error) // CreateConvoyWithBody request with any body - CreateConvoyWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + CreateConvoyWithBody(ctx context.Context, cityName string, params *CreateConvoyParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - CreateConvoy(ctx context.Context, cityName string, body CreateConvoyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + CreateConvoy(ctx context.Context, cityName string, params *CreateConvoyParams, body CreateConvoyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameEvents request GetV0CityByCityNameEvents(ctx context.Context, cityName string, params *GetV0CityByCityNameEventsParams, reqEditors ...RequestEditorFn) (*http.Response, error) // EmitEventWithBody request with any body - EmitEventWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + EmitEventWithBody(ctx context.Context, cityName string, params *EmitEventParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - EmitEvent(ctx context.Context, cityName string, body EmitEventJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + EmitEvent(ctx context.Context, cityName string, params *EmitEventParams, body EmitEventJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // StreamEvents request StreamEvents(ctx context.Context, cityName string, params *StreamEventsParams, reqEditors ...RequestEditorFn) (*http.Response, error) // DeleteV0CityByCityNameExtmsgAdaptersWithBody request with any body - DeleteV0CityByCityNameExtmsgAdaptersWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNameExtmsgAdaptersWithBody(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgAdaptersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - DeleteV0CityByCityNameExtmsgAdapters(ctx context.Context, cityName string, body DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNameExtmsgAdapters(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgAdaptersParams, body DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameExtmsgAdapters request GetV0CityByCityNameExtmsgAdapters(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*http.Response, error) // RegisterExtmsgAdapterWithBody request with any body - RegisterExtmsgAdapterWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + RegisterExtmsgAdapterWithBody(ctx context.Context, cityName string, params *RegisterExtmsgAdapterParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - RegisterExtmsgAdapter(ctx context.Context, cityName string, body RegisterExtmsgAdapterJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + RegisterExtmsgAdapter(ctx context.Context, cityName string, params *RegisterExtmsgAdapterParams, body RegisterExtmsgAdapterJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameExtmsgBindWithBody request with any body - PostV0CityByCityNameExtmsgBindWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameExtmsgBindWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgBindParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameExtmsgBind(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgBindJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameExtmsgBind(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgBindParams, body PostV0CityByCityNameExtmsgBindJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameExtmsgBindings request GetV0CityByCityNameExtmsgBindings(ctx context.Context, cityName string, params *GetV0CityByCityNameExtmsgBindingsParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3779,42 +8076,42 @@ type ClientInterface interface { GetV0CityByCityNameExtmsgGroups(ctx context.Context, cityName string, params *GetV0CityByCityNameExtmsgGroupsParams, reqEditors ...RequestEditorFn) (*http.Response, error) // EnsureExtmsgGroupWithBody request with any body - EnsureExtmsgGroupWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + EnsureExtmsgGroupWithBody(ctx context.Context, cityName string, params *EnsureExtmsgGroupParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - EnsureExtmsgGroup(ctx context.Context, cityName string, body EnsureExtmsgGroupJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + EnsureExtmsgGroup(ctx context.Context, cityName string, params *EnsureExtmsgGroupParams, body EnsureExtmsgGroupJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameExtmsgInboundWithBody request with any body - PostV0CityByCityNameExtmsgInboundWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameExtmsgInboundWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgInboundParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameExtmsgInbound(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgInboundJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameExtmsgInbound(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgInboundParams, body PostV0CityByCityNameExtmsgInboundJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameExtmsgOutboundWithBody request with any body - PostV0CityByCityNameExtmsgOutboundWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameExtmsgOutboundWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgOutboundParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameExtmsgOutbound(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgOutboundJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameExtmsgOutbound(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgOutboundParams, body PostV0CityByCityNameExtmsgOutboundJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // DeleteV0CityByCityNameExtmsgParticipantsWithBody request with any body - DeleteV0CityByCityNameExtmsgParticipantsWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNameExtmsgParticipantsWithBody(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgParticipantsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - DeleteV0CityByCityNameExtmsgParticipants(ctx context.Context, cityName string, body DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNameExtmsgParticipants(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgParticipantsParams, body DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameExtmsgParticipantsWithBody request with any body - PostV0CityByCityNameExtmsgParticipantsWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameExtmsgParticipantsWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgParticipantsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameExtmsgParticipants(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameExtmsgParticipants(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgParticipantsParams, body PostV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameExtmsgTranscript request GetV0CityByCityNameExtmsgTranscript(ctx context.Context, cityName string, params *GetV0CityByCityNameExtmsgTranscriptParams, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameExtmsgTranscriptAckWithBody request with any body - PostV0CityByCityNameExtmsgTranscriptAckWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameExtmsgTranscriptAckWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgTranscriptAckParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameExtmsgTranscriptAck(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameExtmsgTranscriptAck(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgTranscriptAckParams, body PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameExtmsgUnbindWithBody request with any body - PostV0CityByCityNameExtmsgUnbindWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameExtmsgUnbindWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgUnbindParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameExtmsgUnbind(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgUnbindJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameExtmsgUnbind(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgUnbindParams, body PostV0CityByCityNameExtmsgUnbindJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameFormulaByName request GetV0CityByCityNameFormulaByName(ctx context.Context, cityName string, name string, params *GetV0CityByCityNameFormulaByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3829,9 +8126,9 @@ type ClientInterface interface { GetV0CityByCityNameFormulasByName(ctx context.Context, cityName string, name string, params *GetV0CityByCityNameFormulasByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameFormulasByNamePreviewWithBody request with any body - PostV0CityByCityNameFormulasByNamePreviewWithBody(ctx context.Context, cityName string, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameFormulasByNamePreviewWithBody(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameFormulasByNamePreviewParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameFormulasByNamePreview(ctx context.Context, cityName string, name string, body PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameFormulasByNamePreview(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameFormulasByNamePreviewParams, body PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameFormulasByNameRuns request GetV0CityByCityNameFormulasByNameRuns(ctx context.Context, cityName string, name string, params *GetV0CityByCityNameFormulasByNameRunsParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3880,10 +8177,10 @@ type ClientInterface interface { GetV0CityByCityNameOrderByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameOrderByNameDisable request - PostV0CityByCityNameOrderByNameDisable(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameOrderByNameDisable(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameOrderByNameDisableParams, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameOrderByNameEnable request - PostV0CityByCityNameOrderByNameEnable(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameOrderByNameEnable(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameOrderByNameEnableParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameOrders request GetV0CityByCityNameOrders(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3901,13 +8198,13 @@ type ClientInterface interface { GetV0CityByCityNamePacks(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*http.Response, error) // DeleteV0CityByCityNamePatchesAgentByBase request - DeleteV0CityByCityNamePatchesAgentByBase(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNamePatchesAgentByBase(ctx context.Context, cityName string, base string, params *DeleteV0CityByCityNamePatchesAgentByBaseParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNamePatchesAgentByBase request GetV0CityByCityNamePatchesAgentByBase(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) // DeleteV0CityByCityNamePatchesAgentByDirByBase request - DeleteV0CityByCityNamePatchesAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNamePatchesAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, params *DeleteV0CityByCityNamePatchesAgentByDirByBaseParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNamePatchesAgentByDirByBase request GetV0CityByCityNamePatchesAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3916,12 +8213,12 @@ type ClientInterface interface { GetV0CityByCityNamePatchesAgents(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*http.Response, error) // PutV0CityByCityNamePatchesAgentsWithBody request with any body - PutV0CityByCityNamePatchesAgentsWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PutV0CityByCityNamePatchesAgentsWithBody(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesAgentsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PutV0CityByCityNamePatchesAgents(ctx context.Context, cityName string, body PutV0CityByCityNamePatchesAgentsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PutV0CityByCityNamePatchesAgents(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesAgentsParams, body PutV0CityByCityNamePatchesAgentsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // DeleteV0CityByCityNamePatchesProviderByName request - DeleteV0CityByCityNamePatchesProviderByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNamePatchesProviderByName(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNamePatchesProviderByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNamePatchesProviderByName request GetV0CityByCityNamePatchesProviderByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3930,12 +8227,12 @@ type ClientInterface interface { GetV0CityByCityNamePatchesProviders(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*http.Response, error) // PutV0CityByCityNamePatchesProvidersWithBody request with any body - PutV0CityByCityNamePatchesProvidersWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PutV0CityByCityNamePatchesProvidersWithBody(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesProvidersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PutV0CityByCityNamePatchesProviders(ctx context.Context, cityName string, body PutV0CityByCityNamePatchesProvidersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PutV0CityByCityNamePatchesProviders(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesProvidersParams, body PutV0CityByCityNamePatchesProvidersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // DeleteV0CityByCityNamePatchesRigByName request - DeleteV0CityByCityNamePatchesRigByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNamePatchesRigByName(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNamePatchesRigByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNamePatchesRigByName request GetV0CityByCityNamePatchesRigByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3944,31 +8241,31 @@ type ClientInterface interface { GetV0CityByCityNamePatchesRigs(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*http.Response, error) // PutV0CityByCityNamePatchesRigsWithBody request with any body - PutV0CityByCityNamePatchesRigsWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PutV0CityByCityNamePatchesRigsWithBody(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesRigsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PutV0CityByCityNamePatchesRigs(ctx context.Context, cityName string, body PutV0CityByCityNamePatchesRigsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PutV0CityByCityNamePatchesRigs(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesRigsParams, body PutV0CityByCityNamePatchesRigsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameProviderReadiness request GetV0CityByCityNameProviderReadiness(ctx context.Context, cityName string, params *GetV0CityByCityNameProviderReadinessParams, reqEditors ...RequestEditorFn) (*http.Response, error) // DeleteV0CityByCityNameProviderByName request - DeleteV0CityByCityNameProviderByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNameProviderByName(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNameProviderByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameProviderByName request GetV0CityByCityNameProviderByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) // PatchV0CityByCityNameProviderByNameWithBody request with any body - PatchV0CityByCityNameProviderByNameWithBody(ctx context.Context, cityName string, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameProviderByNameWithBody(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameProviderByNameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PatchV0CityByCityNameProviderByName(ctx context.Context, cityName string, name string, body PatchV0CityByCityNameProviderByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameProviderByName(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameProviderByNameParams, body PatchV0CityByCityNameProviderByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameProviders request GetV0CityByCityNameProviders(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*http.Response, error) // CreateProviderWithBody request with any body - CreateProviderWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + CreateProviderWithBody(ctx context.Context, cityName string, params *CreateProviderParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - CreateProvider(ctx context.Context, cityName string, body CreateProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + CreateProvider(ctx context.Context, cityName string, params *CreateProviderParams, body CreateProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameProvidersPublic request GetV0CityByCityNameProvidersPublic(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3977,32 +8274,32 @@ type ClientInterface interface { GetV0CityByCityNameReadiness(ctx context.Context, cityName string, params *GetV0CityByCityNameReadinessParams, reqEditors ...RequestEditorFn) (*http.Response, error) // DeleteV0CityByCityNameRigByName request - DeleteV0CityByCityNameRigByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) + DeleteV0CityByCityNameRigByName(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNameRigByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameRigByName request GetV0CityByCityNameRigByName(ctx context.Context, cityName string, name string, params *GetV0CityByCityNameRigByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) // PatchV0CityByCityNameRigByNameWithBody request with any body - PatchV0CityByCityNameRigByNameWithBody(ctx context.Context, cityName string, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameRigByNameWithBody(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameRigByNameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PatchV0CityByCityNameRigByName(ctx context.Context, cityName string, name string, body PatchV0CityByCityNameRigByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameRigByName(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameRigByNameParams, body PatchV0CityByCityNameRigByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameRigByNameByAction request - PostV0CityByCityNameRigByNameByAction(ctx context.Context, cityName string, name string, action string, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameRigByNameByAction(ctx context.Context, cityName string, name string, action string, params *PostV0CityByCityNameRigByNameByActionParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameRigs request GetV0CityByCityNameRigs(ctx context.Context, cityName string, params *GetV0CityByCityNameRigsParams, reqEditors ...RequestEditorFn) (*http.Response, error) // CreateRigWithBody request with any body - CreateRigWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + CreateRigWithBody(ctx context.Context, cityName string, params *CreateRigParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - CreateRig(ctx context.Context, cityName string, body CreateRigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + CreateRig(ctx context.Context, cityName string, params *CreateRigParams, body CreateRigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameServiceByName request GetV0CityByCityNameServiceByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameServiceByNameRestart request - PostV0CityByCityNameServiceByNameRestart(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameServiceByNameRestart(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameServiceByNameRestartParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameServices request GetV0CityByCityNameServices(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -4011,9 +8308,9 @@ type ClientInterface interface { GetV0CityByCityNameSessionById(ctx context.Context, cityName string, id string, params *GetV0CityByCityNameSessionByIdParams, reqEditors ...RequestEditorFn) (*http.Response, error) // PatchV0CityByCityNameSessionByIdWithBody request with any body - PatchV0CityByCityNameSessionByIdWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameSessionByIdWithBody(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameSessionByIdParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PatchV0CityByCityNameSessionById(ctx context.Context, cityName string, id string, body PatchV0CityByCityNameSessionByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PatchV0CityByCityNameSessionById(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameSessionByIdParams, body PatchV0CityByCityNameSessionByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameSessionByIdAgents request GetV0CityByCityNameSessionByIdAgents(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -4025,62 +8322,65 @@ type ClientInterface interface { PostV0CityByCityNameSessionByIdClose(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdCloseParams, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameSessionByIdKill request - PostV0CityByCityNameSessionByIdKill(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameSessionByIdKill(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdKillParams, reqEditors ...RequestEditorFn) (*http.Response, error) // SendSessionMessageWithBody request with any body - SendSessionMessageWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + SendSessionMessageWithBody(ctx context.Context, cityName string, id string, params *SendSessionMessageParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - SendSessionMessage(ctx context.Context, cityName string, id string, body SendSessionMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + SendSessionMessage(ctx context.Context, cityName string, id string, params *SendSessionMessageParams, body SendSessionMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameSessionByIdPending request GetV0CityByCityNameSessionByIdPending(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameSessionByIdRenameWithBody request with any body - PostV0CityByCityNameSessionByIdRenameWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameSessionByIdRenameWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameSessionByIdRename(ctx context.Context, cityName string, id string, body PostV0CityByCityNameSessionByIdRenameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameSessionByIdRename(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, body PostV0CityByCityNameSessionByIdRenameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // RespondSessionWithBody request with any body - RespondSessionWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + RespondSessionWithBody(ctx context.Context, cityName string, id string, params *RespondSessionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - RespondSession(ctx context.Context, cityName string, id string, body RespondSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + RespondSession(ctx context.Context, cityName string, id string, params *RespondSessionParams, body RespondSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameSessionByIdStop request - PostV0CityByCityNameSessionByIdStop(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameSessionByIdStop(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdStopParams, reqEditors ...RequestEditorFn) (*http.Response, error) // StreamSession request StreamSession(ctx context.Context, cityName string, id string, params *StreamSessionParams, reqEditors ...RequestEditorFn) (*http.Response, error) // SubmitSessionWithBody request with any body - SubmitSessionWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + SubmitSessionWithBody(ctx context.Context, cityName string, id string, params *SubmitSessionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - SubmitSession(ctx context.Context, cityName string, id string, body SubmitSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + SubmitSession(ctx context.Context, cityName string, id string, params *SubmitSessionParams, body SubmitSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameSessionByIdSuspend request - PostV0CityByCityNameSessionByIdSuspend(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameSessionByIdSuspend(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdSuspendParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameSessionByIdTranscript request GetV0CityByCityNameSessionByIdTranscript(ctx context.Context, cityName string, id string, params *GetV0CityByCityNameSessionByIdTranscriptParams, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameSessionByIdWake request - PostV0CityByCityNameSessionByIdWake(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameSessionByIdWake(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdWakeParams, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameSessions request GetV0CityByCityNameSessions(ctx context.Context, cityName string, params *GetV0CityByCityNameSessionsParams, reqEditors ...RequestEditorFn) (*http.Response, error) // CreateSessionWithBody request with any body - CreateSessionWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + CreateSessionWithBody(ctx context.Context, cityName string, params *CreateSessionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - CreateSession(ctx context.Context, cityName string, body CreateSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + CreateSession(ctx context.Context, cityName string, params *CreateSessionParams, body CreateSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // PostV0CityByCityNameSlingWithBody request with any body - PostV0CityByCityNameSlingWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameSlingWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameSlingParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - PostV0CityByCityNameSling(ctx context.Context, cityName string, body PostV0CityByCityNameSlingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + PostV0CityByCityNameSling(ctx context.Context, cityName string, params *PostV0CityByCityNameSlingParams, body PostV0CityByCityNameSlingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // GetV0CityByCityNameStatus request GetV0CityByCityNameStatus(ctx context.Context, cityName string, params *GetV0CityByCityNameStatusParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostV0CityByCityNameUnregister request + PostV0CityByCityNameUnregister(ctx context.Context, cityName string, params *PostV0CityByCityNameUnregisterParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // DeleteV0CityByCityNameWorkflowByWorkflowId request DeleteV0CityByCityNameWorkflowByWorkflowId(ctx context.Context, cityName string, workflowId string, params *DeleteV0CityByCityNameWorkflowByWorkflowIdParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -4124,8 +8424,8 @@ func (c *Client) GetV0Cities(ctx context.Context, reqEditors ...RequestEditorFn) return c.Client.Do(req) } -func (c *Client) PostV0CityWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityRequestWithBody(c.Server, contentType, body) +func (c *Client) PostV0CityWithBody(ctx context.Context, params *PostV0CityParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityRequestWithBody(c.Server, params, contentType, body) if err != nil { return nil, err } @@ -4136,8 +8436,8 @@ func (c *Client) PostV0CityWithBody(ctx context.Context, contentType string, bod return c.Client.Do(req) } -func (c *Client) PostV0City(ctx context.Context, body PostV0CityJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityRequest(c.Server, body) +func (c *Client) PostV0City(ctx context.Context, params *PostV0CityParams, body PostV0CityJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityRequest(c.Server, params, body) if err != nil { return nil, err } @@ -4160,8 +8460,8 @@ func (c *Client) GetV0CityByCityName(ctx context.Context, cityName string, reqEd return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) PatchV0CityByCityNameWithBody(ctx context.Context, cityName string, params *PatchV0CityByCityNameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -4172,8 +8472,8 @@ func (c *Client) PatchV0CityByCityNameWithBody(ctx context.Context, cityName str return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityName(ctx context.Context, cityName string, body PatchV0CityByCityNameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameRequest(c.Server, cityName, body) +func (c *Client) PatchV0CityByCityName(ctx context.Context, cityName string, params *PatchV0CityByCityNameParams, body PatchV0CityByCityNameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -4184,8 +8484,8 @@ func (c *Client) PatchV0CityByCityName(ctx context.Context, cityName string, bod return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNameAgentByBase(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNameAgentByBaseRequest(c.Server, cityName, base) +func (c *Client) DeleteV0CityByCityNameAgentByBase(ctx context.Context, cityName string, base string, params *DeleteV0CityByCityNameAgentByBaseParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNameAgentByBaseRequest(c.Server, cityName, base, params) if err != nil { return nil, err } @@ -4208,8 +8508,8 @@ func (c *Client) GetV0CityByCityNameAgentByBase(ctx context.Context, cityName st return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameAgentByBaseWithBody(ctx context.Context, cityName string, base string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameAgentByBaseRequestWithBody(c.Server, cityName, base, contentType, body) +func (c *Client) PatchV0CityByCityNameAgentByBaseWithBody(ctx context.Context, cityName string, base string, params *PatchV0CityByCityNameAgentByBaseParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameAgentByBaseRequestWithBody(c.Server, cityName, base, params, contentType, body) if err != nil { return nil, err } @@ -4220,8 +8520,8 @@ func (c *Client) PatchV0CityByCityNameAgentByBaseWithBody(ctx context.Context, c return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameAgentByBase(ctx context.Context, cityName string, base string, body PatchV0CityByCityNameAgentByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameAgentByBaseRequest(c.Server, cityName, base, body) +func (c *Client) PatchV0CityByCityNameAgentByBase(ctx context.Context, cityName string, base string, params *PatchV0CityByCityNameAgentByBaseParams, body PatchV0CityByCityNameAgentByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameAgentByBaseRequest(c.Server, cityName, base, params, body) if err != nil { return nil, err } @@ -4256,8 +8556,8 @@ func (c *Client) StreamAgentOutput(ctx context.Context, cityName string, base st return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameAgentByBaseByAction(ctx context.Context, cityName string, base string, action PostV0CityByCityNameAgentByBaseByActionParamsAction, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameAgentByBaseByActionRequest(c.Server, cityName, base, action) +func (c *Client) PostV0CityByCityNameAgentByBaseByAction(ctx context.Context, cityName string, base string, action PostV0CityByCityNameAgentByBaseByActionParamsAction, params *PostV0CityByCityNameAgentByBaseByActionParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameAgentByBaseByActionRequest(c.Server, cityName, base, action, params) if err != nil { return nil, err } @@ -4268,8 +8568,8 @@ func (c *Client) PostV0CityByCityNameAgentByBaseByAction(ctx context.Context, ci return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNameAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNameAgentByDirByBaseRequest(c.Server, cityName, dir, base) +func (c *Client) DeleteV0CityByCityNameAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, params *DeleteV0CityByCityNameAgentByDirByBaseParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNameAgentByDirByBaseRequest(c.Server, cityName, dir, base, params) if err != nil { return nil, err } @@ -4292,8 +8592,8 @@ func (c *Client) GetV0CityByCityNameAgentByDirByBase(ctx context.Context, cityNa return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameAgentByDirByBaseWithBody(ctx context.Context, cityName string, dir string, base string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameAgentByDirByBaseRequestWithBody(c.Server, cityName, dir, base, contentType, body) +func (c *Client) PatchV0CityByCityNameAgentByDirByBaseWithBody(ctx context.Context, cityName string, dir string, base string, params *PatchV0CityByCityNameAgentByDirByBaseParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameAgentByDirByBaseRequestWithBody(c.Server, cityName, dir, base, params, contentType, body) if err != nil { return nil, err } @@ -4304,8 +8604,8 @@ func (c *Client) PatchV0CityByCityNameAgentByDirByBaseWithBody(ctx context.Conte return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, body PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameAgentByDirByBaseRequest(c.Server, cityName, dir, base, body) +func (c *Client) PatchV0CityByCityNameAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, params *PatchV0CityByCityNameAgentByDirByBaseParams, body PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameAgentByDirByBaseRequest(c.Server, cityName, dir, base, params, body) if err != nil { return nil, err } @@ -4340,8 +8640,8 @@ func (c *Client) StreamAgentOutputQualified(ctx context.Context, cityName string return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameAgentByDirByBaseByAction(ctx context.Context, cityName string, dir string, base string, action PostV0CityByCityNameAgentByDirByBaseByActionParamsAction, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameAgentByDirByBaseByActionRequest(c.Server, cityName, dir, base, action) +func (c *Client) PostV0CityByCityNameAgentByDirByBaseByAction(ctx context.Context, cityName string, dir string, base string, action PostV0CityByCityNameAgentByDirByBaseByActionParamsAction, params *PostV0CityByCityNameAgentByDirByBaseByActionParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameAgentByDirByBaseByActionRequest(c.Server, cityName, dir, base, action, params) if err != nil { return nil, err } @@ -4364,8 +8664,8 @@ func (c *Client) GetV0CityByCityNameAgents(ctx context.Context, cityName string, return c.Client.Do(req) } -func (c *Client) CreateAgentWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateAgentRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) CreateAgentWithBody(ctx context.Context, cityName string, params *CreateAgentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateAgentRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -4376,8 +8676,8 @@ func (c *Client) CreateAgentWithBody(ctx context.Context, cityName string, conte return c.Client.Do(req) } -func (c *Client) CreateAgent(ctx context.Context, cityName string, body CreateAgentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateAgentRequest(c.Server, cityName, body) +func (c *Client) CreateAgent(ctx context.Context, cityName string, params *CreateAgentParams, body CreateAgentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateAgentRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -4388,8 +8688,8 @@ func (c *Client) CreateAgent(ctx context.Context, cityName string, body CreateAg return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNameBeadById(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNameBeadByIdRequest(c.Server, cityName, id) +func (c *Client) DeleteV0CityByCityNameBeadById(ctx context.Context, cityName string, id string, params *DeleteV0CityByCityNameBeadByIdParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNameBeadByIdRequest(c.Server, cityName, id, params) if err != nil { return nil, err } @@ -4412,8 +8712,8 @@ func (c *Client) GetV0CityByCityNameBeadById(ctx context.Context, cityName strin return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameBeadByIdWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameBeadByIdRequestWithBody(c.Server, cityName, id, contentType, body) +func (c *Client) PatchV0CityByCityNameBeadByIdWithBody(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameBeadByIdParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameBeadByIdRequestWithBody(c.Server, cityName, id, params, contentType, body) if err != nil { return nil, err } @@ -4424,8 +8724,8 @@ func (c *Client) PatchV0CityByCityNameBeadByIdWithBody(ctx context.Context, city return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameBeadById(ctx context.Context, cityName string, id string, body PatchV0CityByCityNameBeadByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameBeadByIdRequest(c.Server, cityName, id, body) +func (c *Client) PatchV0CityByCityNameBeadById(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameBeadByIdParams, body PatchV0CityByCityNameBeadByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameBeadByIdRequest(c.Server, cityName, id, params, body) if err != nil { return nil, err } @@ -4436,8 +8736,8 @@ func (c *Client) PatchV0CityByCityNameBeadById(ctx context.Context, cityName str return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameBeadByIdAssignWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameBeadByIdAssignRequestWithBody(c.Server, cityName, id, contentType, body) +func (c *Client) PostV0CityByCityNameBeadByIdAssignWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdAssignParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameBeadByIdAssignRequestWithBody(c.Server, cityName, id, params, contentType, body) if err != nil { return nil, err } @@ -4448,8 +8748,8 @@ func (c *Client) PostV0CityByCityNameBeadByIdAssignWithBody(ctx context.Context, return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameBeadByIdAssign(ctx context.Context, cityName string, id string, body PostV0CityByCityNameBeadByIdAssignJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameBeadByIdAssignRequest(c.Server, cityName, id, body) +func (c *Client) PostV0CityByCityNameBeadByIdAssign(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdAssignParams, body PostV0CityByCityNameBeadByIdAssignJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameBeadByIdAssignRequest(c.Server, cityName, id, params, body) if err != nil { return nil, err } @@ -4460,8 +8760,8 @@ func (c *Client) PostV0CityByCityNameBeadByIdAssign(ctx context.Context, cityNam return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameBeadByIdClose(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameBeadByIdCloseRequest(c.Server, cityName, id) +func (c *Client) PostV0CityByCityNameBeadByIdClose(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdCloseParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameBeadByIdCloseRequest(c.Server, cityName, id, params) if err != nil { return nil, err } @@ -4484,8 +8784,8 @@ func (c *Client) GetV0CityByCityNameBeadByIdDeps(ctx context.Context, cityName s return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameBeadByIdReopen(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameBeadByIdReopenRequest(c.Server, cityName, id) +func (c *Client) PostV0CityByCityNameBeadByIdReopen(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdReopenParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameBeadByIdReopenRequest(c.Server, cityName, id, params) if err != nil { return nil, err } @@ -4496,8 +8796,8 @@ func (c *Client) PostV0CityByCityNameBeadByIdReopen(ctx context.Context, cityNam return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameBeadByIdUpdateWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameBeadByIdUpdateRequestWithBody(c.Server, cityName, id, contentType, body) +func (c *Client) PostV0CityByCityNameBeadByIdUpdateWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdUpdateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameBeadByIdUpdateRequestWithBody(c.Server, cityName, id, params, contentType, body) if err != nil { return nil, err } @@ -4508,8 +8808,8 @@ func (c *Client) PostV0CityByCityNameBeadByIdUpdateWithBody(ctx context.Context, return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameBeadByIdUpdate(ctx context.Context, cityName string, id string, body PostV0CityByCityNameBeadByIdUpdateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameBeadByIdUpdateRequest(c.Server, cityName, id, body) +func (c *Client) PostV0CityByCityNameBeadByIdUpdate(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdUpdateParams, body PostV0CityByCityNameBeadByIdUpdateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameBeadByIdUpdateRequest(c.Server, cityName, id, params, body) if err != nil { return nil, err } @@ -4616,8 +8916,8 @@ func (c *Client) GetV0CityByCityNameConfigValidate(ctx context.Context, cityName return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNameConvoyById(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNameConvoyByIdRequest(c.Server, cityName, id) +func (c *Client) DeleteV0CityByCityNameConvoyById(ctx context.Context, cityName string, id string, params *DeleteV0CityByCityNameConvoyByIdParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNameConvoyByIdRequest(c.Server, cityName, id, params) if err != nil { return nil, err } @@ -4640,8 +8940,8 @@ func (c *Client) GetV0CityByCityNameConvoyById(ctx context.Context, cityName str return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameConvoyByIdAddWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameConvoyByIdAddRequestWithBody(c.Server, cityName, id, contentType, body) +func (c *Client) PostV0CityByCityNameConvoyByIdAddWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdAddParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameConvoyByIdAddRequestWithBody(c.Server, cityName, id, params, contentType, body) if err != nil { return nil, err } @@ -4652,8 +8952,8 @@ func (c *Client) PostV0CityByCityNameConvoyByIdAddWithBody(ctx context.Context, return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameConvoyByIdAdd(ctx context.Context, cityName string, id string, body PostV0CityByCityNameConvoyByIdAddJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameConvoyByIdAddRequest(c.Server, cityName, id, body) +func (c *Client) PostV0CityByCityNameConvoyByIdAdd(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdAddParams, body PostV0CityByCityNameConvoyByIdAddJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameConvoyByIdAddRequest(c.Server, cityName, id, params, body) if err != nil { return nil, err } @@ -4676,8 +8976,8 @@ func (c *Client) GetV0CityByCityNameConvoyByIdCheck(ctx context.Context, cityNam return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameConvoyByIdClose(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameConvoyByIdCloseRequest(c.Server, cityName, id) +func (c *Client) PostV0CityByCityNameConvoyByIdClose(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdCloseParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameConvoyByIdCloseRequest(c.Server, cityName, id, params) if err != nil { return nil, err } @@ -4688,8 +8988,8 @@ func (c *Client) PostV0CityByCityNameConvoyByIdClose(ctx context.Context, cityNa return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameConvoyByIdRemoveWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameConvoyByIdRemoveRequestWithBody(c.Server, cityName, id, contentType, body) +func (c *Client) PostV0CityByCityNameConvoyByIdRemoveWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdRemoveParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameConvoyByIdRemoveRequestWithBody(c.Server, cityName, id, params, contentType, body) if err != nil { return nil, err } @@ -4700,8 +9000,8 @@ func (c *Client) PostV0CityByCityNameConvoyByIdRemoveWithBody(ctx context.Contex return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameConvoyByIdRemove(ctx context.Context, cityName string, id string, body PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameConvoyByIdRemoveRequest(c.Server, cityName, id, body) +func (c *Client) PostV0CityByCityNameConvoyByIdRemove(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdRemoveParams, body PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameConvoyByIdRemoveRequest(c.Server, cityName, id, params, body) if err != nil { return nil, err } @@ -4724,8 +9024,8 @@ func (c *Client) GetV0CityByCityNameConvoys(ctx context.Context, cityName string return c.Client.Do(req) } -func (c *Client) CreateConvoyWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateConvoyRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) CreateConvoyWithBody(ctx context.Context, cityName string, params *CreateConvoyParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateConvoyRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -4736,8 +9036,8 @@ func (c *Client) CreateConvoyWithBody(ctx context.Context, cityName string, cont return c.Client.Do(req) } -func (c *Client) CreateConvoy(ctx context.Context, cityName string, body CreateConvoyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateConvoyRequest(c.Server, cityName, body) +func (c *Client) CreateConvoy(ctx context.Context, cityName string, params *CreateConvoyParams, body CreateConvoyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateConvoyRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -4760,8 +9060,8 @@ func (c *Client) GetV0CityByCityNameEvents(ctx context.Context, cityName string, return c.Client.Do(req) } -func (c *Client) EmitEventWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewEmitEventRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) EmitEventWithBody(ctx context.Context, cityName string, params *EmitEventParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewEmitEventRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -4772,8 +9072,8 @@ func (c *Client) EmitEventWithBody(ctx context.Context, cityName string, content return c.Client.Do(req) } -func (c *Client) EmitEvent(ctx context.Context, cityName string, body EmitEventJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewEmitEventRequest(c.Server, cityName, body) +func (c *Client) EmitEvent(ctx context.Context, cityName string, params *EmitEventParams, body EmitEventJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewEmitEventRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -4796,8 +9096,8 @@ func (c *Client) StreamEvents(ctx context.Context, cityName string, params *Stre return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNameExtmsgAdaptersWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNameExtmsgAdaptersRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) DeleteV0CityByCityNameExtmsgAdaptersWithBody(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgAdaptersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNameExtmsgAdaptersRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -4808,8 +9108,8 @@ func (c *Client) DeleteV0CityByCityNameExtmsgAdaptersWithBody(ctx context.Contex return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNameExtmsgAdapters(ctx context.Context, cityName string, body DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNameExtmsgAdaptersRequest(c.Server, cityName, body) +func (c *Client) DeleteV0CityByCityNameExtmsgAdapters(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgAdaptersParams, body DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNameExtmsgAdaptersRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -4832,8 +9132,8 @@ func (c *Client) GetV0CityByCityNameExtmsgAdapters(ctx context.Context, cityName return c.Client.Do(req) } -func (c *Client) RegisterExtmsgAdapterWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewRegisterExtmsgAdapterRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) RegisterExtmsgAdapterWithBody(ctx context.Context, cityName string, params *RegisterExtmsgAdapterParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRegisterExtmsgAdapterRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -4844,8 +9144,8 @@ func (c *Client) RegisterExtmsgAdapterWithBody(ctx context.Context, cityName str return c.Client.Do(req) } -func (c *Client) RegisterExtmsgAdapter(ctx context.Context, cityName string, body RegisterExtmsgAdapterJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewRegisterExtmsgAdapterRequest(c.Server, cityName, body) +func (c *Client) RegisterExtmsgAdapter(ctx context.Context, cityName string, params *RegisterExtmsgAdapterParams, body RegisterExtmsgAdapterJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRegisterExtmsgAdapterRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -4856,8 +9156,8 @@ func (c *Client) RegisterExtmsgAdapter(ctx context.Context, cityName string, bod return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameExtmsgBindWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameExtmsgBindRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) PostV0CityByCityNameExtmsgBindWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgBindParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameExtmsgBindRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -4868,8 +9168,8 @@ func (c *Client) PostV0CityByCityNameExtmsgBindWithBody(ctx context.Context, cit return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameExtmsgBind(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgBindJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameExtmsgBindRequest(c.Server, cityName, body) +func (c *Client) PostV0CityByCityNameExtmsgBind(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgBindParams, body PostV0CityByCityNameExtmsgBindJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameExtmsgBindRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -4904,8 +9204,8 @@ func (c *Client) GetV0CityByCityNameExtmsgGroups(ctx context.Context, cityName s return c.Client.Do(req) } -func (c *Client) EnsureExtmsgGroupWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewEnsureExtmsgGroupRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) EnsureExtmsgGroupWithBody(ctx context.Context, cityName string, params *EnsureExtmsgGroupParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewEnsureExtmsgGroupRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -4916,8 +9216,8 @@ func (c *Client) EnsureExtmsgGroupWithBody(ctx context.Context, cityName string, return c.Client.Do(req) } -func (c *Client) EnsureExtmsgGroup(ctx context.Context, cityName string, body EnsureExtmsgGroupJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewEnsureExtmsgGroupRequest(c.Server, cityName, body) +func (c *Client) EnsureExtmsgGroup(ctx context.Context, cityName string, params *EnsureExtmsgGroupParams, body EnsureExtmsgGroupJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewEnsureExtmsgGroupRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -4928,8 +9228,8 @@ func (c *Client) EnsureExtmsgGroup(ctx context.Context, cityName string, body En return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameExtmsgInboundWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameExtmsgInboundRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) PostV0CityByCityNameExtmsgInboundWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgInboundParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameExtmsgInboundRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -4940,8 +9240,8 @@ func (c *Client) PostV0CityByCityNameExtmsgInboundWithBody(ctx context.Context, return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameExtmsgInbound(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgInboundJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameExtmsgInboundRequest(c.Server, cityName, body) +func (c *Client) PostV0CityByCityNameExtmsgInbound(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgInboundParams, body PostV0CityByCityNameExtmsgInboundJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameExtmsgInboundRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -4952,8 +9252,8 @@ func (c *Client) PostV0CityByCityNameExtmsgInbound(ctx context.Context, cityName return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameExtmsgOutboundWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameExtmsgOutboundRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) PostV0CityByCityNameExtmsgOutboundWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgOutboundParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameExtmsgOutboundRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -4964,8 +9264,8 @@ func (c *Client) PostV0CityByCityNameExtmsgOutboundWithBody(ctx context.Context, return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameExtmsgOutbound(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgOutboundJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameExtmsgOutboundRequest(c.Server, cityName, body) +func (c *Client) PostV0CityByCityNameExtmsgOutbound(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgOutboundParams, body PostV0CityByCityNameExtmsgOutboundJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameExtmsgOutboundRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -4976,8 +9276,8 @@ func (c *Client) PostV0CityByCityNameExtmsgOutbound(ctx context.Context, cityNam return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNameExtmsgParticipantsWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNameExtmsgParticipantsRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) DeleteV0CityByCityNameExtmsgParticipantsWithBody(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgParticipantsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNameExtmsgParticipantsRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -4988,8 +9288,8 @@ func (c *Client) DeleteV0CityByCityNameExtmsgParticipantsWithBody(ctx context.Co return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNameExtmsgParticipants(ctx context.Context, cityName string, body DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNameExtmsgParticipantsRequest(c.Server, cityName, body) +func (c *Client) DeleteV0CityByCityNameExtmsgParticipants(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgParticipantsParams, body DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNameExtmsgParticipantsRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -5000,8 +9300,8 @@ func (c *Client) DeleteV0CityByCityNameExtmsgParticipants(ctx context.Context, c return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameExtmsgParticipantsWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameExtmsgParticipantsRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) PostV0CityByCityNameExtmsgParticipantsWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgParticipantsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameExtmsgParticipantsRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -5012,8 +9312,8 @@ func (c *Client) PostV0CityByCityNameExtmsgParticipantsWithBody(ctx context.Cont return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameExtmsgParticipants(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameExtmsgParticipantsRequest(c.Server, cityName, body) +func (c *Client) PostV0CityByCityNameExtmsgParticipants(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgParticipantsParams, body PostV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameExtmsgParticipantsRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -5036,8 +9336,8 @@ func (c *Client) GetV0CityByCityNameExtmsgTranscript(ctx context.Context, cityNa return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameExtmsgTranscriptAckWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameExtmsgTranscriptAckRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) PostV0CityByCityNameExtmsgTranscriptAckWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgTranscriptAckParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameExtmsgTranscriptAckRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -5048,8 +9348,8 @@ func (c *Client) PostV0CityByCityNameExtmsgTranscriptAckWithBody(ctx context.Con return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameExtmsgTranscriptAck(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameExtmsgTranscriptAckRequest(c.Server, cityName, body) +func (c *Client) PostV0CityByCityNameExtmsgTranscriptAck(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgTranscriptAckParams, body PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameExtmsgTranscriptAckRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -5060,8 +9360,8 @@ func (c *Client) PostV0CityByCityNameExtmsgTranscriptAck(ctx context.Context, ci return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameExtmsgUnbindWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameExtmsgUnbindRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) PostV0CityByCityNameExtmsgUnbindWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgUnbindParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameExtmsgUnbindRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -5072,8 +9372,8 @@ func (c *Client) PostV0CityByCityNameExtmsgUnbindWithBody(ctx context.Context, c return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameExtmsgUnbind(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgUnbindJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameExtmsgUnbindRequest(c.Server, cityName, body) +func (c *Client) PostV0CityByCityNameExtmsgUnbind(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgUnbindParams, body PostV0CityByCityNameExtmsgUnbindJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameExtmsgUnbindRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -5132,8 +9432,8 @@ func (c *Client) GetV0CityByCityNameFormulasByName(ctx context.Context, cityName return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameFormulasByNamePreviewWithBody(ctx context.Context, cityName string, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameFormulasByNamePreviewRequestWithBody(c.Server, cityName, name, contentType, body) +func (c *Client) PostV0CityByCityNameFormulasByNamePreviewWithBody(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameFormulasByNamePreviewParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameFormulasByNamePreviewRequestWithBody(c.Server, cityName, name, params, contentType, body) if err != nil { return nil, err } @@ -5144,8 +9444,8 @@ func (c *Client) PostV0CityByCityNameFormulasByNamePreviewWithBody(ctx context.C return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameFormulasByNamePreview(ctx context.Context, cityName string, name string, body PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameFormulasByNamePreviewRequest(c.Server, cityName, name, body) +func (c *Client) PostV0CityByCityNameFormulasByNamePreview(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameFormulasByNamePreviewParams, body PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameFormulasByNamePreviewRequest(c.Server, cityName, name, params, body) if err != nil { return nil, err } @@ -5348,8 +9648,8 @@ func (c *Client) GetV0CityByCityNameOrderByName(ctx context.Context, cityName st return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameOrderByNameDisable(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameOrderByNameDisableRequest(c.Server, cityName, name) +func (c *Client) PostV0CityByCityNameOrderByNameDisable(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameOrderByNameDisableParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameOrderByNameDisableRequest(c.Server, cityName, name, params) if err != nil { return nil, err } @@ -5360,8 +9660,8 @@ func (c *Client) PostV0CityByCityNameOrderByNameDisable(ctx context.Context, cit return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameOrderByNameEnable(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameOrderByNameEnableRequest(c.Server, cityName, name) +func (c *Client) PostV0CityByCityNameOrderByNameEnable(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameOrderByNameEnableParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameOrderByNameEnableRequest(c.Server, cityName, name, params) if err != nil { return nil, err } @@ -5432,8 +9732,8 @@ func (c *Client) GetV0CityByCityNamePacks(ctx context.Context, cityName string, return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNamePatchesAgentByBase(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNamePatchesAgentByBaseRequest(c.Server, cityName, base) +func (c *Client) DeleteV0CityByCityNamePatchesAgentByBase(ctx context.Context, cityName string, base string, params *DeleteV0CityByCityNamePatchesAgentByBaseParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNamePatchesAgentByBaseRequest(c.Server, cityName, base, params) if err != nil { return nil, err } @@ -5456,8 +9756,8 @@ func (c *Client) GetV0CityByCityNamePatchesAgentByBase(ctx context.Context, city return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNamePatchesAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNamePatchesAgentByDirByBaseRequest(c.Server, cityName, dir, base) +func (c *Client) DeleteV0CityByCityNamePatchesAgentByDirByBase(ctx context.Context, cityName string, dir string, base string, params *DeleteV0CityByCityNamePatchesAgentByDirByBaseParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNamePatchesAgentByDirByBaseRequest(c.Server, cityName, dir, base, params) if err != nil { return nil, err } @@ -5492,8 +9792,8 @@ func (c *Client) GetV0CityByCityNamePatchesAgents(ctx context.Context, cityName return c.Client.Do(req) } -func (c *Client) PutV0CityByCityNamePatchesAgentsWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPutV0CityByCityNamePatchesAgentsRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) PutV0CityByCityNamePatchesAgentsWithBody(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesAgentsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPutV0CityByCityNamePatchesAgentsRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -5504,8 +9804,8 @@ func (c *Client) PutV0CityByCityNamePatchesAgentsWithBody(ctx context.Context, c return c.Client.Do(req) } -func (c *Client) PutV0CityByCityNamePatchesAgents(ctx context.Context, cityName string, body PutV0CityByCityNamePatchesAgentsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPutV0CityByCityNamePatchesAgentsRequest(c.Server, cityName, body) +func (c *Client) PutV0CityByCityNamePatchesAgents(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesAgentsParams, body PutV0CityByCityNamePatchesAgentsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPutV0CityByCityNamePatchesAgentsRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -5516,8 +9816,8 @@ func (c *Client) PutV0CityByCityNamePatchesAgents(ctx context.Context, cityName return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNamePatchesProviderByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNamePatchesProviderByNameRequest(c.Server, cityName, name) +func (c *Client) DeleteV0CityByCityNamePatchesProviderByName(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNamePatchesProviderByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNamePatchesProviderByNameRequest(c.Server, cityName, name, params) if err != nil { return nil, err } @@ -5552,8 +9852,8 @@ func (c *Client) GetV0CityByCityNamePatchesProviders(ctx context.Context, cityNa return c.Client.Do(req) } -func (c *Client) PutV0CityByCityNamePatchesProvidersWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPutV0CityByCityNamePatchesProvidersRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) PutV0CityByCityNamePatchesProvidersWithBody(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesProvidersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPutV0CityByCityNamePatchesProvidersRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -5564,8 +9864,8 @@ func (c *Client) PutV0CityByCityNamePatchesProvidersWithBody(ctx context.Context return c.Client.Do(req) } -func (c *Client) PutV0CityByCityNamePatchesProviders(ctx context.Context, cityName string, body PutV0CityByCityNamePatchesProvidersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPutV0CityByCityNamePatchesProvidersRequest(c.Server, cityName, body) +func (c *Client) PutV0CityByCityNamePatchesProviders(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesProvidersParams, body PutV0CityByCityNamePatchesProvidersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPutV0CityByCityNamePatchesProvidersRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -5576,8 +9876,8 @@ func (c *Client) PutV0CityByCityNamePatchesProviders(ctx context.Context, cityNa return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNamePatchesRigByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNamePatchesRigByNameRequest(c.Server, cityName, name) +func (c *Client) DeleteV0CityByCityNamePatchesRigByName(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNamePatchesRigByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNamePatchesRigByNameRequest(c.Server, cityName, name, params) if err != nil { return nil, err } @@ -5612,8 +9912,8 @@ func (c *Client) GetV0CityByCityNamePatchesRigs(ctx context.Context, cityName st return c.Client.Do(req) } -func (c *Client) PutV0CityByCityNamePatchesRigsWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPutV0CityByCityNamePatchesRigsRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) PutV0CityByCityNamePatchesRigsWithBody(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesRigsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPutV0CityByCityNamePatchesRigsRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -5624,8 +9924,8 @@ func (c *Client) PutV0CityByCityNamePatchesRigsWithBody(ctx context.Context, cit return c.Client.Do(req) } -func (c *Client) PutV0CityByCityNamePatchesRigs(ctx context.Context, cityName string, body PutV0CityByCityNamePatchesRigsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPutV0CityByCityNamePatchesRigsRequest(c.Server, cityName, body) +func (c *Client) PutV0CityByCityNamePatchesRigs(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesRigsParams, body PutV0CityByCityNamePatchesRigsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPutV0CityByCityNamePatchesRigsRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -5648,8 +9948,8 @@ func (c *Client) GetV0CityByCityNameProviderReadiness(ctx context.Context, cityN return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNameProviderByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNameProviderByNameRequest(c.Server, cityName, name) +func (c *Client) DeleteV0CityByCityNameProviderByName(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNameProviderByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNameProviderByNameRequest(c.Server, cityName, name, params) if err != nil { return nil, err } @@ -5672,8 +9972,8 @@ func (c *Client) GetV0CityByCityNameProviderByName(ctx context.Context, cityName return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameProviderByNameWithBody(ctx context.Context, cityName string, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameProviderByNameRequestWithBody(c.Server, cityName, name, contentType, body) +func (c *Client) PatchV0CityByCityNameProviderByNameWithBody(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameProviderByNameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameProviderByNameRequestWithBody(c.Server, cityName, name, params, contentType, body) if err != nil { return nil, err } @@ -5684,8 +9984,8 @@ func (c *Client) PatchV0CityByCityNameProviderByNameWithBody(ctx context.Context return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameProviderByName(ctx context.Context, cityName string, name string, body PatchV0CityByCityNameProviderByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameProviderByNameRequest(c.Server, cityName, name, body) +func (c *Client) PatchV0CityByCityNameProviderByName(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameProviderByNameParams, body PatchV0CityByCityNameProviderByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameProviderByNameRequest(c.Server, cityName, name, params, body) if err != nil { return nil, err } @@ -5708,8 +10008,8 @@ func (c *Client) GetV0CityByCityNameProviders(ctx context.Context, cityName stri return c.Client.Do(req) } -func (c *Client) CreateProviderWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateProviderRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) CreateProviderWithBody(ctx context.Context, cityName string, params *CreateProviderParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateProviderRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -5720,8 +10020,8 @@ func (c *Client) CreateProviderWithBody(ctx context.Context, cityName string, co return c.Client.Do(req) } -func (c *Client) CreateProvider(ctx context.Context, cityName string, body CreateProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateProviderRequest(c.Server, cityName, body) +func (c *Client) CreateProvider(ctx context.Context, cityName string, params *CreateProviderParams, body CreateProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateProviderRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -5756,8 +10056,8 @@ func (c *Client) GetV0CityByCityNameReadiness(ctx context.Context, cityName stri return c.Client.Do(req) } -func (c *Client) DeleteV0CityByCityNameRigByName(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteV0CityByCityNameRigByNameRequest(c.Server, cityName, name) +func (c *Client) DeleteV0CityByCityNameRigByName(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNameRigByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteV0CityByCityNameRigByNameRequest(c.Server, cityName, name, params) if err != nil { return nil, err } @@ -5780,8 +10080,8 @@ func (c *Client) GetV0CityByCityNameRigByName(ctx context.Context, cityName stri return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameRigByNameWithBody(ctx context.Context, cityName string, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameRigByNameRequestWithBody(c.Server, cityName, name, contentType, body) +func (c *Client) PatchV0CityByCityNameRigByNameWithBody(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameRigByNameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameRigByNameRequestWithBody(c.Server, cityName, name, params, contentType, body) if err != nil { return nil, err } @@ -5792,8 +10092,8 @@ func (c *Client) PatchV0CityByCityNameRigByNameWithBody(ctx context.Context, cit return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameRigByName(ctx context.Context, cityName string, name string, body PatchV0CityByCityNameRigByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameRigByNameRequest(c.Server, cityName, name, body) +func (c *Client) PatchV0CityByCityNameRigByName(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameRigByNameParams, body PatchV0CityByCityNameRigByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameRigByNameRequest(c.Server, cityName, name, params, body) if err != nil { return nil, err } @@ -5804,8 +10104,8 @@ func (c *Client) PatchV0CityByCityNameRigByName(ctx context.Context, cityName st return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameRigByNameByAction(ctx context.Context, cityName string, name string, action string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameRigByNameByActionRequest(c.Server, cityName, name, action) +func (c *Client) PostV0CityByCityNameRigByNameByAction(ctx context.Context, cityName string, name string, action string, params *PostV0CityByCityNameRigByNameByActionParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameRigByNameByActionRequest(c.Server, cityName, name, action, params) if err != nil { return nil, err } @@ -5828,8 +10128,8 @@ func (c *Client) GetV0CityByCityNameRigs(ctx context.Context, cityName string, p return c.Client.Do(req) } -func (c *Client) CreateRigWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateRigRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) CreateRigWithBody(ctx context.Context, cityName string, params *CreateRigParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateRigRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -5840,8 +10140,8 @@ func (c *Client) CreateRigWithBody(ctx context.Context, cityName string, content return c.Client.Do(req) } -func (c *Client) CreateRig(ctx context.Context, cityName string, body CreateRigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateRigRequest(c.Server, cityName, body) +func (c *Client) CreateRig(ctx context.Context, cityName string, params *CreateRigParams, body CreateRigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateRigRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -5864,8 +10164,8 @@ func (c *Client) GetV0CityByCityNameServiceByName(ctx context.Context, cityName return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameServiceByNameRestart(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameServiceByNameRestartRequest(c.Server, cityName, name) +func (c *Client) PostV0CityByCityNameServiceByNameRestart(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameServiceByNameRestartParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameServiceByNameRestartRequest(c.Server, cityName, name, params) if err != nil { return nil, err } @@ -5900,8 +10200,8 @@ func (c *Client) GetV0CityByCityNameSessionById(ctx context.Context, cityName st return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameSessionByIdWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameSessionByIdRequestWithBody(c.Server, cityName, id, contentType, body) +func (c *Client) PatchV0CityByCityNameSessionByIdWithBody(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameSessionByIdParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameSessionByIdRequestWithBody(c.Server, cityName, id, params, contentType, body) if err != nil { return nil, err } @@ -5912,8 +10212,8 @@ func (c *Client) PatchV0CityByCityNameSessionByIdWithBody(ctx context.Context, c return c.Client.Do(req) } -func (c *Client) PatchV0CityByCityNameSessionById(ctx context.Context, cityName string, id string, body PatchV0CityByCityNameSessionByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPatchV0CityByCityNameSessionByIdRequest(c.Server, cityName, id, body) +func (c *Client) PatchV0CityByCityNameSessionById(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameSessionByIdParams, body PatchV0CityByCityNameSessionByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV0CityByCityNameSessionByIdRequest(c.Server, cityName, id, params, body) if err != nil { return nil, err } @@ -5960,8 +10260,8 @@ func (c *Client) PostV0CityByCityNameSessionByIdClose(ctx context.Context, cityN return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameSessionByIdKill(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameSessionByIdKillRequest(c.Server, cityName, id) +func (c *Client) PostV0CityByCityNameSessionByIdKill(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdKillParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameSessionByIdKillRequest(c.Server, cityName, id, params) if err != nil { return nil, err } @@ -5972,8 +10272,8 @@ func (c *Client) PostV0CityByCityNameSessionByIdKill(ctx context.Context, cityNa return c.Client.Do(req) } -func (c *Client) SendSessionMessageWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewSendSessionMessageRequestWithBody(c.Server, cityName, id, contentType, body) +func (c *Client) SendSessionMessageWithBody(ctx context.Context, cityName string, id string, params *SendSessionMessageParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSendSessionMessageRequestWithBody(c.Server, cityName, id, params, contentType, body) if err != nil { return nil, err } @@ -5984,8 +10284,8 @@ func (c *Client) SendSessionMessageWithBody(ctx context.Context, cityName string return c.Client.Do(req) } -func (c *Client) SendSessionMessage(ctx context.Context, cityName string, id string, body SendSessionMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewSendSessionMessageRequest(c.Server, cityName, id, body) +func (c *Client) SendSessionMessage(ctx context.Context, cityName string, id string, params *SendSessionMessageParams, body SendSessionMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSendSessionMessageRequest(c.Server, cityName, id, params, body) if err != nil { return nil, err } @@ -6008,8 +10308,8 @@ func (c *Client) GetV0CityByCityNameSessionByIdPending(ctx context.Context, city return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameSessionByIdRenameWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameSessionByIdRenameRequestWithBody(c.Server, cityName, id, contentType, body) +func (c *Client) PostV0CityByCityNameSessionByIdRenameWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameSessionByIdRenameRequestWithBody(c.Server, cityName, id, params, contentType, body) if err != nil { return nil, err } @@ -6020,8 +10320,8 @@ func (c *Client) PostV0CityByCityNameSessionByIdRenameWithBody(ctx context.Conte return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameSessionByIdRename(ctx context.Context, cityName string, id string, body PostV0CityByCityNameSessionByIdRenameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameSessionByIdRenameRequest(c.Server, cityName, id, body) +func (c *Client) PostV0CityByCityNameSessionByIdRename(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, body PostV0CityByCityNameSessionByIdRenameJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameSessionByIdRenameRequest(c.Server, cityName, id, params, body) if err != nil { return nil, err } @@ -6032,8 +10332,8 @@ func (c *Client) PostV0CityByCityNameSessionByIdRename(ctx context.Context, city return c.Client.Do(req) } -func (c *Client) RespondSessionWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewRespondSessionRequestWithBody(c.Server, cityName, id, contentType, body) +func (c *Client) RespondSessionWithBody(ctx context.Context, cityName string, id string, params *RespondSessionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRespondSessionRequestWithBody(c.Server, cityName, id, params, contentType, body) if err != nil { return nil, err } @@ -6044,8 +10344,8 @@ func (c *Client) RespondSessionWithBody(ctx context.Context, cityName string, id return c.Client.Do(req) } -func (c *Client) RespondSession(ctx context.Context, cityName string, id string, body RespondSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewRespondSessionRequest(c.Server, cityName, id, body) +func (c *Client) RespondSession(ctx context.Context, cityName string, id string, params *RespondSessionParams, body RespondSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRespondSessionRequest(c.Server, cityName, id, params, body) if err != nil { return nil, err } @@ -6056,8 +10356,8 @@ func (c *Client) RespondSession(ctx context.Context, cityName string, id string, return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameSessionByIdStop(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameSessionByIdStopRequest(c.Server, cityName, id) +func (c *Client) PostV0CityByCityNameSessionByIdStop(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdStopParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameSessionByIdStopRequest(c.Server, cityName, id, params) if err != nil { return nil, err } @@ -6080,8 +10380,8 @@ func (c *Client) StreamSession(ctx context.Context, cityName string, id string, return c.Client.Do(req) } -func (c *Client) SubmitSessionWithBody(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewSubmitSessionRequestWithBody(c.Server, cityName, id, contentType, body) +func (c *Client) SubmitSessionWithBody(ctx context.Context, cityName string, id string, params *SubmitSessionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSubmitSessionRequestWithBody(c.Server, cityName, id, params, contentType, body) if err != nil { return nil, err } @@ -6092,8 +10392,8 @@ func (c *Client) SubmitSessionWithBody(ctx context.Context, cityName string, id return c.Client.Do(req) } -func (c *Client) SubmitSession(ctx context.Context, cityName string, id string, body SubmitSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewSubmitSessionRequest(c.Server, cityName, id, body) +func (c *Client) SubmitSession(ctx context.Context, cityName string, id string, params *SubmitSessionParams, body SubmitSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSubmitSessionRequest(c.Server, cityName, id, params, body) if err != nil { return nil, err } @@ -6104,8 +10404,8 @@ func (c *Client) SubmitSession(ctx context.Context, cityName string, id string, return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameSessionByIdSuspend(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameSessionByIdSuspendRequest(c.Server, cityName, id) +func (c *Client) PostV0CityByCityNameSessionByIdSuspend(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdSuspendParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameSessionByIdSuspendRequest(c.Server, cityName, id, params) if err != nil { return nil, err } @@ -6128,8 +10428,8 @@ func (c *Client) GetV0CityByCityNameSessionByIdTranscript(ctx context.Context, c return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameSessionByIdWake(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameSessionByIdWakeRequest(c.Server, cityName, id) +func (c *Client) PostV0CityByCityNameSessionByIdWake(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdWakeParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameSessionByIdWakeRequest(c.Server, cityName, id, params) if err != nil { return nil, err } @@ -6152,8 +10452,8 @@ func (c *Client) GetV0CityByCityNameSessions(ctx context.Context, cityName strin return c.Client.Do(req) } -func (c *Client) CreateSessionWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateSessionRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) CreateSessionWithBody(ctx context.Context, cityName string, params *CreateSessionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateSessionRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -6164,8 +10464,8 @@ func (c *Client) CreateSessionWithBody(ctx context.Context, cityName string, con return c.Client.Do(req) } -func (c *Client) CreateSession(ctx context.Context, cityName string, body CreateSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateSessionRequest(c.Server, cityName, body) +func (c *Client) CreateSession(ctx context.Context, cityName string, params *CreateSessionParams, body CreateSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateSessionRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -6176,8 +10476,8 @@ func (c *Client) CreateSession(ctx context.Context, cityName string, body Create return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameSlingWithBody(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameSlingRequestWithBody(c.Server, cityName, contentType, body) +func (c *Client) PostV0CityByCityNameSlingWithBody(ctx context.Context, cityName string, params *PostV0CityByCityNameSlingParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameSlingRequestWithBody(c.Server, cityName, params, contentType, body) if err != nil { return nil, err } @@ -6188,8 +10488,8 @@ func (c *Client) PostV0CityByCityNameSlingWithBody(ctx context.Context, cityName return c.Client.Do(req) } -func (c *Client) PostV0CityByCityNameSling(ctx context.Context, cityName string, body PostV0CityByCityNameSlingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostV0CityByCityNameSlingRequest(c.Server, cityName, body) +func (c *Client) PostV0CityByCityNameSling(ctx context.Context, cityName string, params *PostV0CityByCityNameSlingParams, body PostV0CityByCityNameSlingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameSlingRequest(c.Server, cityName, params, body) if err != nil { return nil, err } @@ -6212,6 +10512,18 @@ func (c *Client) GetV0CityByCityNameStatus(ctx context.Context, cityName string, return c.Client.Do(req) } +func (c *Client) PostV0CityByCityNameUnregister(ctx context.Context, cityName string, params *PostV0CityByCityNameUnregisterParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameUnregisterRequest(c.Server, cityName, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) DeleteV0CityByCityNameWorkflowByWorkflowId(ctx context.Context, cityName string, workflowId string, params *DeleteV0CityByCityNameWorkflowByWorkflowIdParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeleteV0CityByCityNameWorkflowByWorkflowIdRequest(c.Server, cityName, workflowId, params) if err != nil { @@ -6339,18 +10651,18 @@ func NewGetV0CitiesRequest(server string) (*http.Request, error) { } // NewPostV0CityRequest calls the generic PostV0City builder with application/json body -func NewPostV0CityRequest(server string, body PostV0CityJSONRequestBody) (*http.Request, error) { +func NewPostV0CityRequest(server string, params *PostV0CityParams, body PostV0CityJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityRequestWithBody(server, "application/json", bodyReader) + return NewPostV0CityRequestWithBody(server, params, "application/json", bodyReader) } // NewPostV0CityRequestWithBody generates requests for PostV0City with any type of body -func NewPostV0CityRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityRequestWithBody(server string, params *PostV0CityParams, contentType string, body io.Reader) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -6375,6 +10687,19 @@ func NewPostV0CityRequestWithBody(server string, contentType string, body io.Rea req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -6413,18 +10738,18 @@ func NewGetV0CityByCityNameRequest(server string, cityName string) (*http.Reques } // NewPatchV0CityByCityNameRequest calls the generic PatchV0CityByCityName builder with application/json body -func NewPatchV0CityByCityNameRequest(server string, cityName string, body PatchV0CityByCityNameJSONRequestBody) (*http.Request, error) { +func NewPatchV0CityByCityNameRequest(server string, cityName string, params *PatchV0CityByCityNameParams, body PatchV0CityByCityNameJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPatchV0CityByCityNameRequestWithBody(server, cityName, "application/json", bodyReader) + return NewPatchV0CityByCityNameRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewPatchV0CityByCityNameRequestWithBody generates requests for PatchV0CityByCityName with any type of body -func NewPatchV0CityByCityNameRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewPatchV0CityByCityNameRequestWithBody(server string, cityName string, params *PatchV0CityByCityNameParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -6456,11 +10781,24 @@ func NewPatchV0CityByCityNameRequestWithBody(server string, cityName string, con req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewDeleteV0CityByCityNameAgentByBaseRequest generates requests for DeleteV0CityByCityNameAgentByBase -func NewDeleteV0CityByCityNameAgentByBaseRequest(server string, cityName string, base string) (*http.Request, error) { +func NewDeleteV0CityByCityNameAgentByBaseRequest(server string, cityName string, base string, params *DeleteV0CityByCityNameAgentByBaseParams) (*http.Request, error) { var err error var pathParam0 string @@ -6497,6 +10835,19 @@ func NewDeleteV0CityByCityNameAgentByBaseRequest(server string, cityName string, return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -6542,18 +10893,18 @@ func NewGetV0CityByCityNameAgentByBaseRequest(server string, cityName string, ba } // NewPatchV0CityByCityNameAgentByBaseRequest calls the generic PatchV0CityByCityNameAgentByBase builder with application/json body -func NewPatchV0CityByCityNameAgentByBaseRequest(server string, cityName string, base string, body PatchV0CityByCityNameAgentByBaseJSONRequestBody) (*http.Request, error) { +func NewPatchV0CityByCityNameAgentByBaseRequest(server string, cityName string, base string, params *PatchV0CityByCityNameAgentByBaseParams, body PatchV0CityByCityNameAgentByBaseJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPatchV0CityByCityNameAgentByBaseRequestWithBody(server, cityName, base, "application/json", bodyReader) + return NewPatchV0CityByCityNameAgentByBaseRequestWithBody(server, cityName, base, params, "application/json", bodyReader) } // NewPatchV0CityByCityNameAgentByBaseRequestWithBody generates requests for PatchV0CityByCityNameAgentByBase with any type of body -func NewPatchV0CityByCityNameAgentByBaseRequestWithBody(server string, cityName string, base string, contentType string, body io.Reader) (*http.Request, error) { +func NewPatchV0CityByCityNameAgentByBaseRequestWithBody(server string, cityName string, base string, params *PatchV0CityByCityNameAgentByBaseParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -6580,18 +10931,31 @@ func NewPatchV0CityByCityNameAgentByBaseRequestWithBody(server string, cityName operationPath = "." + operationPath } - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PATCH", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) - req, err := http.NewRequest("PATCH", queryURL.String(), body) - if err != nil { - return nil, err } - req.Header.Add("Content-Type", contentType) - return req, nil } @@ -6716,7 +11080,7 @@ func NewStreamAgentOutputRequest(server string, cityName string, base string) (* } // NewPostV0CityByCityNameAgentByBaseByActionRequest generates requests for PostV0CityByCityNameAgentByBaseByAction -func NewPostV0CityByCityNameAgentByBaseByActionRequest(server string, cityName string, base string, action PostV0CityByCityNameAgentByBaseByActionParamsAction) (*http.Request, error) { +func NewPostV0CityByCityNameAgentByBaseByActionRequest(server string, cityName string, base string, action PostV0CityByCityNameAgentByBaseByActionParamsAction, params *PostV0CityByCityNameAgentByBaseByActionParams) (*http.Request, error) { var err error var pathParam0 string @@ -6760,11 +11124,24 @@ func NewPostV0CityByCityNameAgentByBaseByActionRequest(server string, cityName s return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewDeleteV0CityByCityNameAgentByDirByBaseRequest generates requests for DeleteV0CityByCityNameAgentByDirByBase -func NewDeleteV0CityByCityNameAgentByDirByBaseRequest(server string, cityName string, dir string, base string) (*http.Request, error) { +func NewDeleteV0CityByCityNameAgentByDirByBaseRequest(server string, cityName string, dir string, base string, params *DeleteV0CityByCityNameAgentByDirByBaseParams) (*http.Request, error) { var err error var pathParam0 string @@ -6808,6 +11185,19 @@ func NewDeleteV0CityByCityNameAgentByDirByBaseRequest(server string, cityName st return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -6860,18 +11250,18 @@ func NewGetV0CityByCityNameAgentByDirByBaseRequest(server string, cityName strin } // NewPatchV0CityByCityNameAgentByDirByBaseRequest calls the generic PatchV0CityByCityNameAgentByDirByBase builder with application/json body -func NewPatchV0CityByCityNameAgentByDirByBaseRequest(server string, cityName string, dir string, base string, body PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody) (*http.Request, error) { +func NewPatchV0CityByCityNameAgentByDirByBaseRequest(server string, cityName string, dir string, base string, params *PatchV0CityByCityNameAgentByDirByBaseParams, body PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPatchV0CityByCityNameAgentByDirByBaseRequestWithBody(server, cityName, dir, base, "application/json", bodyReader) + return NewPatchV0CityByCityNameAgentByDirByBaseRequestWithBody(server, cityName, dir, base, params, "application/json", bodyReader) } // NewPatchV0CityByCityNameAgentByDirByBaseRequestWithBody generates requests for PatchV0CityByCityNameAgentByDirByBase with any type of body -func NewPatchV0CityByCityNameAgentByDirByBaseRequestWithBody(server string, cityName string, dir string, base string, contentType string, body io.Reader) (*http.Request, error) { +func NewPatchV0CityByCityNameAgentByDirByBaseRequestWithBody(server string, cityName string, dir string, base string, params *PatchV0CityByCityNameAgentByDirByBaseParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -6917,6 +11307,19 @@ func NewPatchV0CityByCityNameAgentByDirByBaseRequestWithBody(server string, city req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -7055,7 +11458,7 @@ func NewStreamAgentOutputQualifiedRequest(server string, cityName string, dir st } // NewPostV0CityByCityNameAgentByDirByBaseByActionRequest generates requests for PostV0CityByCityNameAgentByDirByBaseByAction -func NewPostV0CityByCityNameAgentByDirByBaseByActionRequest(server string, cityName string, dir string, base string, action PostV0CityByCityNameAgentByDirByBaseByActionParamsAction) (*http.Request, error) { +func NewPostV0CityByCityNameAgentByDirByBaseByActionRequest(server string, cityName string, dir string, base string, action PostV0CityByCityNameAgentByDirByBaseByActionParamsAction, params *PostV0CityByCityNameAgentByDirByBaseByActionParams) (*http.Request, error) { var err error var pathParam0 string @@ -7106,6 +11509,19 @@ func NewPostV0CityByCityNameAgentByDirByBaseByActionRequest(server string, cityN return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -7246,18 +11662,18 @@ func NewGetV0CityByCityNameAgentsRequest(server string, cityName string, params } // NewCreateAgentRequest calls the generic CreateAgent builder with application/json body -func NewCreateAgentRequest(server string, cityName string, body CreateAgentJSONRequestBody) (*http.Request, error) { +func NewCreateAgentRequest(server string, cityName string, params *CreateAgentParams, body CreateAgentJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewCreateAgentRequestWithBody(server, cityName, "application/json", bodyReader) + return NewCreateAgentRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewCreateAgentRequestWithBody generates requests for CreateAgent with any type of body -func NewCreateAgentRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewCreateAgentRequestWithBody(server string, cityName string, params *CreateAgentParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -7289,11 +11705,24 @@ func NewCreateAgentRequestWithBody(server string, cityName string, contentType s req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewDeleteV0CityByCityNameBeadByIdRequest generates requests for DeleteV0CityByCityNameBeadById -func NewDeleteV0CityByCityNameBeadByIdRequest(server string, cityName string, id string) (*http.Request, error) { +func NewDeleteV0CityByCityNameBeadByIdRequest(server string, cityName string, id string, params *DeleteV0CityByCityNameBeadByIdParams) (*http.Request, error) { var err error var pathParam0 string @@ -7330,6 +11759,19 @@ func NewDeleteV0CityByCityNameBeadByIdRequest(server string, cityName string, id return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -7375,18 +11817,18 @@ func NewGetV0CityByCityNameBeadByIdRequest(server string, cityName string, id st } // NewPatchV0CityByCityNameBeadByIdRequest calls the generic PatchV0CityByCityNameBeadById builder with application/json body -func NewPatchV0CityByCityNameBeadByIdRequest(server string, cityName string, id string, body PatchV0CityByCityNameBeadByIdJSONRequestBody) (*http.Request, error) { +func NewPatchV0CityByCityNameBeadByIdRequest(server string, cityName string, id string, params *PatchV0CityByCityNameBeadByIdParams, body PatchV0CityByCityNameBeadByIdJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPatchV0CityByCityNameBeadByIdRequestWithBody(server, cityName, id, "application/json", bodyReader) + return NewPatchV0CityByCityNameBeadByIdRequestWithBody(server, cityName, id, params, "application/json", bodyReader) } // NewPatchV0CityByCityNameBeadByIdRequestWithBody generates requests for PatchV0CityByCityNameBeadById with any type of body -func NewPatchV0CityByCityNameBeadByIdRequestWithBody(server string, cityName string, id string, contentType string, body io.Reader) (*http.Request, error) { +func NewPatchV0CityByCityNameBeadByIdRequestWithBody(server string, cityName string, id string, params *PatchV0CityByCityNameBeadByIdParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -7425,22 +11867,35 @@ func NewPatchV0CityByCityNameBeadByIdRequestWithBody(server string, cityName str req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameBeadByIdAssignRequest calls the generic PostV0CityByCityNameBeadByIdAssign builder with application/json body -func NewPostV0CityByCityNameBeadByIdAssignRequest(server string, cityName string, id string, body PostV0CityByCityNameBeadByIdAssignJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameBeadByIdAssignRequest(server string, cityName string, id string, params *PostV0CityByCityNameBeadByIdAssignParams, body PostV0CityByCityNameBeadByIdAssignJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameBeadByIdAssignRequestWithBody(server, cityName, id, "application/json", bodyReader) + return NewPostV0CityByCityNameBeadByIdAssignRequestWithBody(server, cityName, id, params, "application/json", bodyReader) } // NewPostV0CityByCityNameBeadByIdAssignRequestWithBody generates requests for PostV0CityByCityNameBeadByIdAssign with any type of body -func NewPostV0CityByCityNameBeadByIdAssignRequestWithBody(server string, cityName string, id string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameBeadByIdAssignRequestWithBody(server string, cityName string, id string, params *PostV0CityByCityNameBeadByIdAssignParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -7479,11 +11934,24 @@ func NewPostV0CityByCityNameBeadByIdAssignRequestWithBody(server string, cityNam req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameBeadByIdCloseRequest generates requests for PostV0CityByCityNameBeadByIdClose -func NewPostV0CityByCityNameBeadByIdCloseRequest(server string, cityName string, id string) (*http.Request, error) { +func NewPostV0CityByCityNameBeadByIdCloseRequest(server string, cityName string, id string, params *PostV0CityByCityNameBeadByIdCloseParams) (*http.Request, error) { var err error var pathParam0 string @@ -7520,6 +11988,19 @@ func NewPostV0CityByCityNameBeadByIdCloseRequest(server string, cityName string, return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -7565,7 +12046,7 @@ func NewGetV0CityByCityNameBeadByIdDepsRequest(server string, cityName string, i } // NewPostV0CityByCityNameBeadByIdReopenRequest generates requests for PostV0CityByCityNameBeadByIdReopen -func NewPostV0CityByCityNameBeadByIdReopenRequest(server string, cityName string, id string) (*http.Request, error) { +func NewPostV0CityByCityNameBeadByIdReopenRequest(server string, cityName string, id string, params *PostV0CityByCityNameBeadByIdReopenParams) (*http.Request, error) { var err error var pathParam0 string @@ -7602,22 +12083,35 @@ func NewPostV0CityByCityNameBeadByIdReopenRequest(server string, cityName string return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameBeadByIdUpdateRequest calls the generic PostV0CityByCityNameBeadByIdUpdate builder with application/json body -func NewPostV0CityByCityNameBeadByIdUpdateRequest(server string, cityName string, id string, body PostV0CityByCityNameBeadByIdUpdateJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameBeadByIdUpdateRequest(server string, cityName string, id string, params *PostV0CityByCityNameBeadByIdUpdateParams, body PostV0CityByCityNameBeadByIdUpdateJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameBeadByIdUpdateRequestWithBody(server, cityName, id, "application/json", bodyReader) + return NewPostV0CityByCityNameBeadByIdUpdateRequestWithBody(server, cityName, id, params, "application/json", bodyReader) } // NewPostV0CityByCityNameBeadByIdUpdateRequestWithBody generates requests for PostV0CityByCityNameBeadByIdUpdate with any type of body -func NewPostV0CityByCityNameBeadByIdUpdateRequestWithBody(server string, cityName string, id string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameBeadByIdUpdateRequestWithBody(server string, cityName string, id string, params *PostV0CityByCityNameBeadByIdUpdateParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -7656,6 +12150,19 @@ func NewPostV0CityByCityNameBeadByIdUpdateRequestWithBody(server string, cityNam req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -7889,15 +12396,24 @@ func NewCreateBeadRequestWithBody(server string, cityName string, params *Create if params != nil { + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + if params.IdempotencyKey != nil { - var headerParam0 string + var headerParam1 string - headerParam0, err = runtime.StyleParamWithOptions("simple", false, "Idempotency-Key", *params.IdempotencyKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + headerParam1, err = runtime.StyleParamWithOptions("simple", false, "Idempotency-Key", *params.IdempotencyKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) if err != nil { return nil, err } - req.Header.Set("Idempotency-Key", headerParam0) + req.Header.Set("Idempotency-Key", headerParam1) } } @@ -8121,7 +12637,7 @@ func NewGetV0CityByCityNameConfigValidateRequest(server string, cityName string) } // NewDeleteV0CityByCityNameConvoyByIdRequest generates requests for DeleteV0CityByCityNameConvoyById -func NewDeleteV0CityByCityNameConvoyByIdRequest(server string, cityName string, id string) (*http.Request, error) { +func NewDeleteV0CityByCityNameConvoyByIdRequest(server string, cityName string, id string, params *DeleteV0CityByCityNameConvoyByIdParams) (*http.Request, error) { var err error var pathParam0 string @@ -8158,6 +12674,19 @@ func NewDeleteV0CityByCityNameConvoyByIdRequest(server string, cityName string, return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -8203,18 +12732,18 @@ func NewGetV0CityByCityNameConvoyByIdRequest(server string, cityName string, id } // NewPostV0CityByCityNameConvoyByIdAddRequest calls the generic PostV0CityByCityNameConvoyByIdAdd builder with application/json body -func NewPostV0CityByCityNameConvoyByIdAddRequest(server string, cityName string, id string, body PostV0CityByCityNameConvoyByIdAddJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameConvoyByIdAddRequest(server string, cityName string, id string, params *PostV0CityByCityNameConvoyByIdAddParams, body PostV0CityByCityNameConvoyByIdAddJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameConvoyByIdAddRequestWithBody(server, cityName, id, "application/json", bodyReader) + return NewPostV0CityByCityNameConvoyByIdAddRequestWithBody(server, cityName, id, params, "application/json", bodyReader) } // NewPostV0CityByCityNameConvoyByIdAddRequestWithBody generates requests for PostV0CityByCityNameConvoyByIdAdd with any type of body -func NewPostV0CityByCityNameConvoyByIdAddRequestWithBody(server string, cityName string, id string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameConvoyByIdAddRequestWithBody(server string, cityName string, id string, params *PostV0CityByCityNameConvoyByIdAddParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -8253,6 +12782,19 @@ func NewPostV0CityByCityNameConvoyByIdAddRequestWithBody(server string, cityName req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -8298,7 +12840,7 @@ func NewGetV0CityByCityNameConvoyByIdCheckRequest(server string, cityName string } // NewPostV0CityByCityNameConvoyByIdCloseRequest generates requests for PostV0CityByCityNameConvoyByIdClose -func NewPostV0CityByCityNameConvoyByIdCloseRequest(server string, cityName string, id string) (*http.Request, error) { +func NewPostV0CityByCityNameConvoyByIdCloseRequest(server string, cityName string, id string, params *PostV0CityByCityNameConvoyByIdCloseParams) (*http.Request, error) { var err error var pathParam0 string @@ -8335,22 +12877,35 @@ func NewPostV0CityByCityNameConvoyByIdCloseRequest(server string, cityName strin return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameConvoyByIdRemoveRequest calls the generic PostV0CityByCityNameConvoyByIdRemove builder with application/json body -func NewPostV0CityByCityNameConvoyByIdRemoveRequest(server string, cityName string, id string, body PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameConvoyByIdRemoveRequest(server string, cityName string, id string, params *PostV0CityByCityNameConvoyByIdRemoveParams, body PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameConvoyByIdRemoveRequestWithBody(server, cityName, id, "application/json", bodyReader) + return NewPostV0CityByCityNameConvoyByIdRemoveRequestWithBody(server, cityName, id, params, "application/json", bodyReader) } // NewPostV0CityByCityNameConvoyByIdRemoveRequestWithBody generates requests for PostV0CityByCityNameConvoyByIdRemove with any type of body -func NewPostV0CityByCityNameConvoyByIdRemoveRequestWithBody(server string, cityName string, id string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameConvoyByIdRemoveRequestWithBody(server string, cityName string, id string, params *PostV0CityByCityNameConvoyByIdRemoveParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -8389,6 +12944,19 @@ func NewPostV0CityByCityNameConvoyByIdRemoveRequestWithBody(server string, cityN req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -8497,18 +13065,18 @@ func NewGetV0CityByCityNameConvoysRequest(server string, cityName string, params } // NewCreateConvoyRequest calls the generic CreateConvoy builder with application/json body -func NewCreateConvoyRequest(server string, cityName string, body CreateConvoyJSONRequestBody) (*http.Request, error) { +func NewCreateConvoyRequest(server string, cityName string, params *CreateConvoyParams, body CreateConvoyJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewCreateConvoyRequestWithBody(server, cityName, "application/json", bodyReader) + return NewCreateConvoyRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewCreateConvoyRequestWithBody generates requests for CreateConvoy with any type of body -func NewCreateConvoyRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewCreateConvoyRequestWithBody(server string, cityName string, params *CreateConvoyParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -8540,6 +13108,19 @@ func NewCreateConvoyRequestWithBody(server string, cityName string, contentType req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -8696,18 +13277,18 @@ func NewGetV0CityByCityNameEventsRequest(server string, cityName string, params } // NewEmitEventRequest calls the generic EmitEvent builder with application/json body -func NewEmitEventRequest(server string, cityName string, body EmitEventJSONRequestBody) (*http.Request, error) { +func NewEmitEventRequest(server string, cityName string, params *EmitEventParams, body EmitEventJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewEmitEventRequestWithBody(server, cityName, "application/json", bodyReader) + return NewEmitEventRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewEmitEventRequestWithBody generates requests for EmitEvent with any type of body -func NewEmitEventRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewEmitEventRequestWithBody(server string, cityName string, params *EmitEventParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -8739,6 +13320,19 @@ func NewEmitEventRequestWithBody(server string, cityName string, contentType str req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -8814,18 +13408,18 @@ func NewStreamEventsRequest(server string, cityName string, params *StreamEvents } // NewDeleteV0CityByCityNameExtmsgAdaptersRequest calls the generic DeleteV0CityByCityNameExtmsgAdapters builder with application/json body -func NewDeleteV0CityByCityNameExtmsgAdaptersRequest(server string, cityName string, body DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody) (*http.Request, error) { +func NewDeleteV0CityByCityNameExtmsgAdaptersRequest(server string, cityName string, params *DeleteV0CityByCityNameExtmsgAdaptersParams, body DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewDeleteV0CityByCityNameExtmsgAdaptersRequestWithBody(server, cityName, "application/json", bodyReader) + return NewDeleteV0CityByCityNameExtmsgAdaptersRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewDeleteV0CityByCityNameExtmsgAdaptersRequestWithBody generates requests for DeleteV0CityByCityNameExtmsgAdapters with any type of body -func NewDeleteV0CityByCityNameExtmsgAdaptersRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewDeleteV0CityByCityNameExtmsgAdaptersRequestWithBody(server string, cityName string, params *DeleteV0CityByCityNameExtmsgAdaptersParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -8857,6 +13451,19 @@ func NewDeleteV0CityByCityNameExtmsgAdaptersRequestWithBody(server string, cityN req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -8895,18 +13502,18 @@ func NewGetV0CityByCityNameExtmsgAdaptersRequest(server string, cityName string) } // NewRegisterExtmsgAdapterRequest calls the generic RegisterExtmsgAdapter builder with application/json body -func NewRegisterExtmsgAdapterRequest(server string, cityName string, body RegisterExtmsgAdapterJSONRequestBody) (*http.Request, error) { +func NewRegisterExtmsgAdapterRequest(server string, cityName string, params *RegisterExtmsgAdapterParams, body RegisterExtmsgAdapterJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewRegisterExtmsgAdapterRequestWithBody(server, cityName, "application/json", bodyReader) + return NewRegisterExtmsgAdapterRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewRegisterExtmsgAdapterRequestWithBody generates requests for RegisterExtmsgAdapter with any type of body -func NewRegisterExtmsgAdapterRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewRegisterExtmsgAdapterRequestWithBody(server string, cityName string, params *RegisterExtmsgAdapterParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -8938,22 +13545,35 @@ func NewRegisterExtmsgAdapterRequestWithBody(server string, cityName string, con req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameExtmsgBindRequest calls the generic PostV0CityByCityNameExtmsgBind builder with application/json body -func NewPostV0CityByCityNameExtmsgBindRequest(server string, cityName string, body PostV0CityByCityNameExtmsgBindJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameExtmsgBindRequest(server string, cityName string, params *PostV0CityByCityNameExtmsgBindParams, body PostV0CityByCityNameExtmsgBindJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameExtmsgBindRequestWithBody(server, cityName, "application/json", bodyReader) + return NewPostV0CityByCityNameExtmsgBindRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewPostV0CityByCityNameExtmsgBindRequestWithBody generates requests for PostV0CityByCityNameExtmsgBind with any type of body -func NewPostV0CityByCityNameExtmsgBindRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameExtmsgBindRequestWithBody(server string, cityName string, params *PostV0CityByCityNameExtmsgBindParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -8985,6 +13605,19 @@ func NewPostV0CityByCityNameExtmsgBindRequestWithBody(server string, cityName st req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -9165,18 +13798,18 @@ func NewGetV0CityByCityNameExtmsgGroupsRequest(server string, cityName string, p } // NewEnsureExtmsgGroupRequest calls the generic EnsureExtmsgGroup builder with application/json body -func NewEnsureExtmsgGroupRequest(server string, cityName string, body EnsureExtmsgGroupJSONRequestBody) (*http.Request, error) { +func NewEnsureExtmsgGroupRequest(server string, cityName string, params *EnsureExtmsgGroupParams, body EnsureExtmsgGroupJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewEnsureExtmsgGroupRequestWithBody(server, cityName, "application/json", bodyReader) + return NewEnsureExtmsgGroupRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewEnsureExtmsgGroupRequestWithBody generates requests for EnsureExtmsgGroup with any type of body -func NewEnsureExtmsgGroupRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewEnsureExtmsgGroupRequestWithBody(server string, cityName string, params *EnsureExtmsgGroupParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -9208,22 +13841,35 @@ func NewEnsureExtmsgGroupRequestWithBody(server string, cityName string, content req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameExtmsgInboundRequest calls the generic PostV0CityByCityNameExtmsgInbound builder with application/json body -func NewPostV0CityByCityNameExtmsgInboundRequest(server string, cityName string, body PostV0CityByCityNameExtmsgInboundJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameExtmsgInboundRequest(server string, cityName string, params *PostV0CityByCityNameExtmsgInboundParams, body PostV0CityByCityNameExtmsgInboundJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameExtmsgInboundRequestWithBody(server, cityName, "application/json", bodyReader) + return NewPostV0CityByCityNameExtmsgInboundRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewPostV0CityByCityNameExtmsgInboundRequestWithBody generates requests for PostV0CityByCityNameExtmsgInbound with any type of body -func NewPostV0CityByCityNameExtmsgInboundRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameExtmsgInboundRequestWithBody(server string, cityName string, params *PostV0CityByCityNameExtmsgInboundParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -9255,22 +13901,35 @@ func NewPostV0CityByCityNameExtmsgInboundRequestWithBody(server string, cityName req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameExtmsgOutboundRequest calls the generic PostV0CityByCityNameExtmsgOutbound builder with application/json body -func NewPostV0CityByCityNameExtmsgOutboundRequest(server string, cityName string, body PostV0CityByCityNameExtmsgOutboundJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameExtmsgOutboundRequest(server string, cityName string, params *PostV0CityByCityNameExtmsgOutboundParams, body PostV0CityByCityNameExtmsgOutboundJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameExtmsgOutboundRequestWithBody(server, cityName, "application/json", bodyReader) + return NewPostV0CityByCityNameExtmsgOutboundRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewPostV0CityByCityNameExtmsgOutboundRequestWithBody generates requests for PostV0CityByCityNameExtmsgOutbound with any type of body -func NewPostV0CityByCityNameExtmsgOutboundRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameExtmsgOutboundRequestWithBody(server string, cityName string, params *PostV0CityByCityNameExtmsgOutboundParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -9302,22 +13961,35 @@ func NewPostV0CityByCityNameExtmsgOutboundRequestWithBody(server string, cityNam req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewDeleteV0CityByCityNameExtmsgParticipantsRequest calls the generic DeleteV0CityByCityNameExtmsgParticipants builder with application/json body -func NewDeleteV0CityByCityNameExtmsgParticipantsRequest(server string, cityName string, body DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody) (*http.Request, error) { +func NewDeleteV0CityByCityNameExtmsgParticipantsRequest(server string, cityName string, params *DeleteV0CityByCityNameExtmsgParticipantsParams, body DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewDeleteV0CityByCityNameExtmsgParticipantsRequestWithBody(server, cityName, "application/json", bodyReader) + return NewDeleteV0CityByCityNameExtmsgParticipantsRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewDeleteV0CityByCityNameExtmsgParticipantsRequestWithBody generates requests for DeleteV0CityByCityNameExtmsgParticipants with any type of body -func NewDeleteV0CityByCityNameExtmsgParticipantsRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewDeleteV0CityByCityNameExtmsgParticipantsRequestWithBody(server string, cityName string, params *DeleteV0CityByCityNameExtmsgParticipantsParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -9347,24 +14019,37 @@ func NewDeleteV0CityByCityNameExtmsgParticipantsRequestWithBody(server string, c return nil, err } - req.Header.Add("Content-Type", contentType) + req.Header.Add("Content-Type", contentType) + + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } return req, nil } // NewPostV0CityByCityNameExtmsgParticipantsRequest calls the generic PostV0CityByCityNameExtmsgParticipants builder with application/json body -func NewPostV0CityByCityNameExtmsgParticipantsRequest(server string, cityName string, body PostV0CityByCityNameExtmsgParticipantsJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameExtmsgParticipantsRequest(server string, cityName string, params *PostV0CityByCityNameExtmsgParticipantsParams, body PostV0CityByCityNameExtmsgParticipantsJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameExtmsgParticipantsRequestWithBody(server, cityName, "application/json", bodyReader) + return NewPostV0CityByCityNameExtmsgParticipantsRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewPostV0CityByCityNameExtmsgParticipantsRequestWithBody generates requests for PostV0CityByCityNameExtmsgParticipants with any type of body -func NewPostV0CityByCityNameExtmsgParticipantsRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameExtmsgParticipantsRequestWithBody(server string, cityName string, params *PostV0CityByCityNameExtmsgParticipantsParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -9396,6 +14081,19 @@ func NewPostV0CityByCityNameExtmsgParticipantsRequestWithBody(server string, cit req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -9536,18 +14234,18 @@ func NewGetV0CityByCityNameExtmsgTranscriptRequest(server string, cityName strin } // NewPostV0CityByCityNameExtmsgTranscriptAckRequest calls the generic PostV0CityByCityNameExtmsgTranscriptAck builder with application/json body -func NewPostV0CityByCityNameExtmsgTranscriptAckRequest(server string, cityName string, body PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameExtmsgTranscriptAckRequest(server string, cityName string, params *PostV0CityByCityNameExtmsgTranscriptAckParams, body PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameExtmsgTranscriptAckRequestWithBody(server, cityName, "application/json", bodyReader) + return NewPostV0CityByCityNameExtmsgTranscriptAckRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewPostV0CityByCityNameExtmsgTranscriptAckRequestWithBody generates requests for PostV0CityByCityNameExtmsgTranscriptAck with any type of body -func NewPostV0CityByCityNameExtmsgTranscriptAckRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameExtmsgTranscriptAckRequestWithBody(server string, cityName string, params *PostV0CityByCityNameExtmsgTranscriptAckParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -9579,22 +14277,35 @@ func NewPostV0CityByCityNameExtmsgTranscriptAckRequestWithBody(server string, ci req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameExtmsgUnbindRequest calls the generic PostV0CityByCityNameExtmsgUnbind builder with application/json body -func NewPostV0CityByCityNameExtmsgUnbindRequest(server string, cityName string, body PostV0CityByCityNameExtmsgUnbindJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameExtmsgUnbindRequest(server string, cityName string, params *PostV0CityByCityNameExtmsgUnbindParams, body PostV0CityByCityNameExtmsgUnbindJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameExtmsgUnbindRequestWithBody(server, cityName, "application/json", bodyReader) + return NewPostV0CityByCityNameExtmsgUnbindRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewPostV0CityByCityNameExtmsgUnbindRequestWithBody generates requests for PostV0CityByCityNameExtmsgUnbind with any type of body -func NewPostV0CityByCityNameExtmsgUnbindRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameExtmsgUnbindRequestWithBody(server string, cityName string, params *PostV0CityByCityNameExtmsgUnbindParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -9626,6 +14337,19 @@ func NewPostV0CityByCityNameExtmsgUnbindRequestWithBody(server string, cityName req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -9972,18 +14696,18 @@ func NewGetV0CityByCityNameFormulasByNameRequest(server string, cityName string, } // NewPostV0CityByCityNameFormulasByNamePreviewRequest calls the generic PostV0CityByCityNameFormulasByNamePreview builder with application/json body -func NewPostV0CityByCityNameFormulasByNamePreviewRequest(server string, cityName string, name string, body PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameFormulasByNamePreviewRequest(server string, cityName string, name string, params *PostV0CityByCityNameFormulasByNamePreviewParams, body PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameFormulasByNamePreviewRequestWithBody(server, cityName, name, "application/json", bodyReader) + return NewPostV0CityByCityNameFormulasByNamePreviewRequestWithBody(server, cityName, name, params, "application/json", bodyReader) } // NewPostV0CityByCityNameFormulasByNamePreviewRequestWithBody generates requests for PostV0CityByCityNameFormulasByNamePreview with any type of body -func NewPostV0CityByCityNameFormulasByNamePreviewRequestWithBody(server string, cityName string, name string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameFormulasByNamePreviewRequestWithBody(server string, cityName string, name string, params *PostV0CityByCityNameFormulasByNamePreviewParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -10022,6 +14746,19 @@ func NewPostV0CityByCityNameFormulasByNamePreviewRequestWithBody(server string, req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -10352,15 +15089,24 @@ func NewSendMailRequestWithBody(server string, cityName string, params *SendMail if params != nil { + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + if params.IdempotencyKey != nil { - var headerParam0 string + var headerParam1 string - headerParam0, err = runtime.StyleParamWithOptions("simple", false, "Idempotency-Key", *params.IdempotencyKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + headerParam1, err = runtime.StyleParamWithOptions("simple", false, "Idempotency-Key", *params.IdempotencyKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) if err != nil { return nil, err } - req.Header.Set("Idempotency-Key", headerParam0) + req.Header.Set("Idempotency-Key", headerParam1) } } @@ -10563,6 +15309,19 @@ func NewDeleteV0CityByCityNameMailByIdRequest(server string, cityName string, id return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -10689,6 +15448,19 @@ func NewPostV0CityByCityNameMailByIdArchiveRequest(server string, cityName strin return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -10752,6 +15524,19 @@ func NewPostV0CityByCityNameMailByIdMarkUnreadRequest(server string, cityName st return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -10815,6 +15600,19 @@ func NewPostV0CityByCityNameMailByIdReadRequest(server string, cityName string, return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -10891,6 +15689,19 @@ func NewReplyMailRequestWithBody(server string, cityName string, id string, para req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -10999,7 +15810,7 @@ func NewGetV0CityByCityNameOrderByNameRequest(server string, cityName string, na } // NewPostV0CityByCityNameOrderByNameDisableRequest generates requests for PostV0CityByCityNameOrderByNameDisable -func NewPostV0CityByCityNameOrderByNameDisableRequest(server string, cityName string, name string) (*http.Request, error) { +func NewPostV0CityByCityNameOrderByNameDisableRequest(server string, cityName string, name string, params *PostV0CityByCityNameOrderByNameDisableParams) (*http.Request, error) { var err error var pathParam0 string @@ -11036,11 +15847,24 @@ func NewPostV0CityByCityNameOrderByNameDisableRequest(server string, cityName st return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameOrderByNameEnableRequest generates requests for PostV0CityByCityNameOrderByNameEnable -func NewPostV0CityByCityNameOrderByNameEnableRequest(server string, cityName string, name string) (*http.Request, error) { +func NewPostV0CityByCityNameOrderByNameEnableRequest(server string, cityName string, name string, params *PostV0CityByCityNameOrderByNameEnableParams) (*http.Request, error) { var err error var pathParam0 string @@ -11077,6 +15901,19 @@ func NewPostV0CityByCityNameOrderByNameEnableRequest(server string, cityName str return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -11355,7 +16192,7 @@ func NewGetV0CityByCityNamePacksRequest(server string, cityName string) (*http.R } // NewDeleteV0CityByCityNamePatchesAgentByBaseRequest generates requests for DeleteV0CityByCityNamePatchesAgentByBase -func NewDeleteV0CityByCityNamePatchesAgentByBaseRequest(server string, cityName string, base string) (*http.Request, error) { +func NewDeleteV0CityByCityNamePatchesAgentByBaseRequest(server string, cityName string, base string, params *DeleteV0CityByCityNamePatchesAgentByBaseParams) (*http.Request, error) { var err error var pathParam0 string @@ -11392,6 +16229,19 @@ func NewDeleteV0CityByCityNamePatchesAgentByBaseRequest(server string, cityName return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -11437,7 +16287,7 @@ func NewGetV0CityByCityNamePatchesAgentByBaseRequest(server string, cityName str } // NewDeleteV0CityByCityNamePatchesAgentByDirByBaseRequest generates requests for DeleteV0CityByCityNamePatchesAgentByDirByBase -func NewDeleteV0CityByCityNamePatchesAgentByDirByBaseRequest(server string, cityName string, dir string, base string) (*http.Request, error) { +func NewDeleteV0CityByCityNamePatchesAgentByDirByBaseRequest(server string, cityName string, dir string, base string, params *DeleteV0CityByCityNamePatchesAgentByDirByBaseParams) (*http.Request, error) { var err error var pathParam0 string @@ -11481,6 +16331,19 @@ func NewDeleteV0CityByCityNamePatchesAgentByDirByBaseRequest(server string, city return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -11567,18 +16430,18 @@ func NewGetV0CityByCityNamePatchesAgentsRequest(server string, cityName string) } // NewPutV0CityByCityNamePatchesAgentsRequest calls the generic PutV0CityByCityNamePatchesAgents builder with application/json body -func NewPutV0CityByCityNamePatchesAgentsRequest(server string, cityName string, body PutV0CityByCityNamePatchesAgentsJSONRequestBody) (*http.Request, error) { +func NewPutV0CityByCityNamePatchesAgentsRequest(server string, cityName string, params *PutV0CityByCityNamePatchesAgentsParams, body PutV0CityByCityNamePatchesAgentsJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPutV0CityByCityNamePatchesAgentsRequestWithBody(server, cityName, "application/json", bodyReader) + return NewPutV0CityByCityNamePatchesAgentsRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewPutV0CityByCityNamePatchesAgentsRequestWithBody generates requests for PutV0CityByCityNamePatchesAgents with any type of body -func NewPutV0CityByCityNamePatchesAgentsRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewPutV0CityByCityNamePatchesAgentsRequestWithBody(server string, cityName string, params *PutV0CityByCityNamePatchesAgentsParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -11610,11 +16473,24 @@ func NewPutV0CityByCityNamePatchesAgentsRequestWithBody(server string, cityName req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewDeleteV0CityByCityNamePatchesProviderByNameRequest generates requests for DeleteV0CityByCityNamePatchesProviderByName -func NewDeleteV0CityByCityNamePatchesProviderByNameRequest(server string, cityName string, name string) (*http.Request, error) { +func NewDeleteV0CityByCityNamePatchesProviderByNameRequest(server string, cityName string, name string, params *DeleteV0CityByCityNamePatchesProviderByNameParams) (*http.Request, error) { var err error var pathParam0 string @@ -11651,6 +16527,19 @@ func NewDeleteV0CityByCityNamePatchesProviderByNameRequest(server string, cityNa return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -11730,18 +16619,18 @@ func NewGetV0CityByCityNamePatchesProvidersRequest(server string, cityName strin } // NewPutV0CityByCityNamePatchesProvidersRequest calls the generic PutV0CityByCityNamePatchesProviders builder with application/json body -func NewPutV0CityByCityNamePatchesProvidersRequest(server string, cityName string, body PutV0CityByCityNamePatchesProvidersJSONRequestBody) (*http.Request, error) { +func NewPutV0CityByCityNamePatchesProvidersRequest(server string, cityName string, params *PutV0CityByCityNamePatchesProvidersParams, body PutV0CityByCityNamePatchesProvidersJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPutV0CityByCityNamePatchesProvidersRequestWithBody(server, cityName, "application/json", bodyReader) + return NewPutV0CityByCityNamePatchesProvidersRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewPutV0CityByCityNamePatchesProvidersRequestWithBody generates requests for PutV0CityByCityNamePatchesProviders with any type of body -func NewPutV0CityByCityNamePatchesProvidersRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewPutV0CityByCityNamePatchesProvidersRequestWithBody(server string, cityName string, params *PutV0CityByCityNamePatchesProvidersParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -11773,11 +16662,24 @@ func NewPutV0CityByCityNamePatchesProvidersRequestWithBody(server string, cityNa req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewDeleteV0CityByCityNamePatchesRigByNameRequest generates requests for DeleteV0CityByCityNamePatchesRigByName -func NewDeleteV0CityByCityNamePatchesRigByNameRequest(server string, cityName string, name string) (*http.Request, error) { +func NewDeleteV0CityByCityNamePatchesRigByNameRequest(server string, cityName string, name string, params *DeleteV0CityByCityNamePatchesRigByNameParams) (*http.Request, error) { var err error var pathParam0 string @@ -11814,6 +16716,19 @@ func NewDeleteV0CityByCityNamePatchesRigByNameRequest(server string, cityName st return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -11893,18 +16808,18 @@ func NewGetV0CityByCityNamePatchesRigsRequest(server string, cityName string) (* } // NewPutV0CityByCityNamePatchesRigsRequest calls the generic PutV0CityByCityNamePatchesRigs builder with application/json body -func NewPutV0CityByCityNamePatchesRigsRequest(server string, cityName string, body PutV0CityByCityNamePatchesRigsJSONRequestBody) (*http.Request, error) { +func NewPutV0CityByCityNamePatchesRigsRequest(server string, cityName string, params *PutV0CityByCityNamePatchesRigsParams, body PutV0CityByCityNamePatchesRigsJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPutV0CityByCityNamePatchesRigsRequestWithBody(server, cityName, "application/json", bodyReader) + return NewPutV0CityByCityNamePatchesRigsRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewPutV0CityByCityNamePatchesRigsRequestWithBody generates requests for PutV0CityByCityNamePatchesRigs with any type of body -func NewPutV0CityByCityNamePatchesRigsRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewPutV0CityByCityNamePatchesRigsRequestWithBody(server string, cityName string, params *PutV0CityByCityNamePatchesRigsParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -11936,6 +16851,19 @@ func NewPutV0CityByCityNamePatchesRigsRequestWithBody(server string, cityName st req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -12012,7 +16940,7 @@ func NewGetV0CityByCityNameProviderReadinessRequest(server string, cityName stri } // NewDeleteV0CityByCityNameProviderByNameRequest generates requests for DeleteV0CityByCityNameProviderByName -func NewDeleteV0CityByCityNameProviderByNameRequest(server string, cityName string, name string) (*http.Request, error) { +func NewDeleteV0CityByCityNameProviderByNameRequest(server string, cityName string, name string, params *DeleteV0CityByCityNameProviderByNameParams) (*http.Request, error) { var err error var pathParam0 string @@ -12049,6 +16977,19 @@ func NewDeleteV0CityByCityNameProviderByNameRequest(server string, cityName stri return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -12094,18 +17035,18 @@ func NewGetV0CityByCityNameProviderByNameRequest(server string, cityName string, } // NewPatchV0CityByCityNameProviderByNameRequest calls the generic PatchV0CityByCityNameProviderByName builder with application/json body -func NewPatchV0CityByCityNameProviderByNameRequest(server string, cityName string, name string, body PatchV0CityByCityNameProviderByNameJSONRequestBody) (*http.Request, error) { +func NewPatchV0CityByCityNameProviderByNameRequest(server string, cityName string, name string, params *PatchV0CityByCityNameProviderByNameParams, body PatchV0CityByCityNameProviderByNameJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPatchV0CityByCityNameProviderByNameRequestWithBody(server, cityName, name, "application/json", bodyReader) + return NewPatchV0CityByCityNameProviderByNameRequestWithBody(server, cityName, name, params, "application/json", bodyReader) } // NewPatchV0CityByCityNameProviderByNameRequestWithBody generates requests for PatchV0CityByCityNameProviderByName with any type of body -func NewPatchV0CityByCityNameProviderByNameRequestWithBody(server string, cityName string, name string, contentType string, body io.Reader) (*http.Request, error) { +func NewPatchV0CityByCityNameProviderByNameRequestWithBody(server string, cityName string, name string, params *PatchV0CityByCityNameProviderByNameParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -12144,6 +17085,19 @@ func NewPatchV0CityByCityNameProviderByNameRequestWithBody(server string, cityNa req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -12182,18 +17136,18 @@ func NewGetV0CityByCityNameProvidersRequest(server string, cityName string) (*ht } // NewCreateProviderRequest calls the generic CreateProvider builder with application/json body -func NewCreateProviderRequest(server string, cityName string, body CreateProviderJSONRequestBody) (*http.Request, error) { +func NewCreateProviderRequest(server string, cityName string, params *CreateProviderParams, body CreateProviderJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewCreateProviderRequestWithBody(server, cityName, "application/json", bodyReader) + return NewCreateProviderRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewCreateProviderRequestWithBody generates requests for CreateProvider with any type of body -func NewCreateProviderRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewCreateProviderRequestWithBody(server string, cityName string, params *CreateProviderParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -12225,6 +17179,19 @@ func NewCreateProviderRequestWithBody(server string, cityName string, contentTyp req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -12335,7 +17302,7 @@ func NewGetV0CityByCityNameReadinessRequest(server string, cityName string, para } // NewDeleteV0CityByCityNameRigByNameRequest generates requests for DeleteV0CityByCityNameRigByName -func NewDeleteV0CityByCityNameRigByNameRequest(server string, cityName string, name string) (*http.Request, error) { +func NewDeleteV0CityByCityNameRigByNameRequest(server string, cityName string, name string, params *DeleteV0CityByCityNameRigByNameParams) (*http.Request, error) { var err error var pathParam0 string @@ -12372,6 +17339,19 @@ func NewDeleteV0CityByCityNameRigByNameRequest(server string, cityName string, n return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -12439,18 +17419,18 @@ func NewGetV0CityByCityNameRigByNameRequest(server string, cityName string, name } // NewPatchV0CityByCityNameRigByNameRequest calls the generic PatchV0CityByCityNameRigByName builder with application/json body -func NewPatchV0CityByCityNameRigByNameRequest(server string, cityName string, name string, body PatchV0CityByCityNameRigByNameJSONRequestBody) (*http.Request, error) { +func NewPatchV0CityByCityNameRigByNameRequest(server string, cityName string, name string, params *PatchV0CityByCityNameRigByNameParams, body PatchV0CityByCityNameRigByNameJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPatchV0CityByCityNameRigByNameRequestWithBody(server, cityName, name, "application/json", bodyReader) + return NewPatchV0CityByCityNameRigByNameRequestWithBody(server, cityName, name, params, "application/json", bodyReader) } // NewPatchV0CityByCityNameRigByNameRequestWithBody generates requests for PatchV0CityByCityNameRigByName with any type of body -func NewPatchV0CityByCityNameRigByNameRequestWithBody(server string, cityName string, name string, contentType string, body io.Reader) (*http.Request, error) { +func NewPatchV0CityByCityNameRigByNameRequestWithBody(server string, cityName string, name string, params *PatchV0CityByCityNameRigByNameParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -12489,11 +17469,24 @@ func NewPatchV0CityByCityNameRigByNameRequestWithBody(server string, cityName st req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameRigByNameByActionRequest generates requests for PostV0CityByCityNameRigByNameByAction -func NewPostV0CityByCityNameRigByNameByActionRequest(server string, cityName string, name string, action string) (*http.Request, error) { +func NewPostV0CityByCityNameRigByNameByActionRequest(server string, cityName string, name string, action string, params *PostV0CityByCityNameRigByNameByActionParams) (*http.Request, error) { var err error var pathParam0 string @@ -12527,14 +17520,27 @@ func NewPostV0CityByCityNameRigByNameByActionRequest(server string, cityName str operationPath = "." + operationPath } - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), nil) + if err != nil { + return nil, err + } + + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) - req, err := http.NewRequest("POST", queryURL.String(), nil) - if err != nil { - return nil, err } return req, nil @@ -12629,18 +17635,18 @@ func NewGetV0CityByCityNameRigsRequest(server string, cityName string, params *G } // NewCreateRigRequest calls the generic CreateRig builder with application/json body -func NewCreateRigRequest(server string, cityName string, body CreateRigJSONRequestBody) (*http.Request, error) { +func NewCreateRigRequest(server string, cityName string, params *CreateRigParams, body CreateRigJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewCreateRigRequestWithBody(server, cityName, "application/json", bodyReader) + return NewCreateRigRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewCreateRigRequestWithBody generates requests for CreateRig with any type of body -func NewCreateRigRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewCreateRigRequestWithBody(server string, cityName string, params *CreateRigParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -12672,6 +17678,19 @@ func NewCreateRigRequestWithBody(server string, cityName string, contentType str req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -12717,7 +17736,7 @@ func NewGetV0CityByCityNameServiceByNameRequest(server string, cityName string, } // NewPostV0CityByCityNameServiceByNameRestartRequest generates requests for PostV0CityByCityNameServiceByNameRestart -func NewPostV0CityByCityNameServiceByNameRestartRequest(server string, cityName string, name string) (*http.Request, error) { +func NewPostV0CityByCityNameServiceByNameRestartRequest(server string, cityName string, name string, params *PostV0CityByCityNameServiceByNameRestartParams) (*http.Request, error) { var err error var pathParam0 string @@ -12754,6 +17773,19 @@ func NewPostV0CityByCityNameServiceByNameRestartRequest(server string, cityName return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -12855,18 +17887,18 @@ func NewGetV0CityByCityNameSessionByIdRequest(server string, cityName string, id } // NewPatchV0CityByCityNameSessionByIdRequest calls the generic PatchV0CityByCityNameSessionById builder with application/json body -func NewPatchV0CityByCityNameSessionByIdRequest(server string, cityName string, id string, body PatchV0CityByCityNameSessionByIdJSONRequestBody) (*http.Request, error) { +func NewPatchV0CityByCityNameSessionByIdRequest(server string, cityName string, id string, params *PatchV0CityByCityNameSessionByIdParams, body PatchV0CityByCityNameSessionByIdJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPatchV0CityByCityNameSessionByIdRequestWithBody(server, cityName, id, "application/json", bodyReader) + return NewPatchV0CityByCityNameSessionByIdRequestWithBody(server, cityName, id, params, "application/json", bodyReader) } // NewPatchV0CityByCityNameSessionByIdRequestWithBody generates requests for PatchV0CityByCityNameSessionById with any type of body -func NewPatchV0CityByCityNameSessionByIdRequestWithBody(server string, cityName string, id string, contentType string, body io.Reader) (*http.Request, error) { +func NewPatchV0CityByCityNameSessionByIdRequestWithBody(server string, cityName string, id string, params *PatchV0CityByCityNameSessionByIdParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -12905,6 +17937,19 @@ func NewPatchV0CityByCityNameSessionByIdRequestWithBody(server string, cityName req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -13057,11 +18102,24 @@ func NewPostV0CityByCityNameSessionByIdCloseRequest(server string, cityName stri return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameSessionByIdKillRequest generates requests for PostV0CityByCityNameSessionByIdKill -func NewPostV0CityByCityNameSessionByIdKillRequest(server string, cityName string, id string) (*http.Request, error) { +func NewPostV0CityByCityNameSessionByIdKillRequest(server string, cityName string, id string, params *PostV0CityByCityNameSessionByIdKillParams) (*http.Request, error) { var err error var pathParam0 string @@ -13098,22 +18156,35 @@ func NewPostV0CityByCityNameSessionByIdKillRequest(server string, cityName strin return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewSendSessionMessageRequest calls the generic SendSessionMessage builder with application/json body -func NewSendSessionMessageRequest(server string, cityName string, id string, body SendSessionMessageJSONRequestBody) (*http.Request, error) { +func NewSendSessionMessageRequest(server string, cityName string, id string, params *SendSessionMessageParams, body SendSessionMessageJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewSendSessionMessageRequestWithBody(server, cityName, id, "application/json", bodyReader) + return NewSendSessionMessageRequestWithBody(server, cityName, id, params, "application/json", bodyReader) } // NewSendSessionMessageRequestWithBody generates requests for SendSessionMessage with any type of body -func NewSendSessionMessageRequestWithBody(server string, cityName string, id string, contentType string, body io.Reader) (*http.Request, error) { +func NewSendSessionMessageRequestWithBody(server string, cityName string, id string, params *SendSessionMessageParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -13152,6 +18223,19 @@ func NewSendSessionMessageRequestWithBody(server string, cityName string, id str req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -13197,18 +18281,18 @@ func NewGetV0CityByCityNameSessionByIdPendingRequest(server string, cityName str } // NewPostV0CityByCityNameSessionByIdRenameRequest calls the generic PostV0CityByCityNameSessionByIdRename builder with application/json body -func NewPostV0CityByCityNameSessionByIdRenameRequest(server string, cityName string, id string, body PostV0CityByCityNameSessionByIdRenameJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameSessionByIdRenameRequest(server string, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, body PostV0CityByCityNameSessionByIdRenameJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameSessionByIdRenameRequestWithBody(server, cityName, id, "application/json", bodyReader) + return NewPostV0CityByCityNameSessionByIdRenameRequestWithBody(server, cityName, id, params, "application/json", bodyReader) } // NewPostV0CityByCityNameSessionByIdRenameRequestWithBody generates requests for PostV0CityByCityNameSessionByIdRename with any type of body -func NewPostV0CityByCityNameSessionByIdRenameRequestWithBody(server string, cityName string, id string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameSessionByIdRenameRequestWithBody(server string, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -13247,22 +18331,35 @@ func NewPostV0CityByCityNameSessionByIdRenameRequestWithBody(server string, city req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewRespondSessionRequest calls the generic RespondSession builder with application/json body -func NewRespondSessionRequest(server string, cityName string, id string, body RespondSessionJSONRequestBody) (*http.Request, error) { +func NewRespondSessionRequest(server string, cityName string, id string, params *RespondSessionParams, body RespondSessionJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewRespondSessionRequestWithBody(server, cityName, id, "application/json", bodyReader) + return NewRespondSessionRequestWithBody(server, cityName, id, params, "application/json", bodyReader) } // NewRespondSessionRequestWithBody generates requests for RespondSession with any type of body -func NewRespondSessionRequestWithBody(server string, cityName string, id string, contentType string, body io.Reader) (*http.Request, error) { +func NewRespondSessionRequestWithBody(server string, cityName string, id string, params *RespondSessionParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -13301,11 +18398,24 @@ func NewRespondSessionRequestWithBody(server string, cityName string, id string, req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameSessionByIdStopRequest generates requests for PostV0CityByCityNameSessionByIdStop -func NewPostV0CityByCityNameSessionByIdStopRequest(server string, cityName string, id string) (*http.Request, error) { +func NewPostV0CityByCityNameSessionByIdStopRequest(server string, cityName string, id string, params *PostV0CityByCityNameSessionByIdStopParams) (*http.Request, error) { var err error var pathParam0 string @@ -13342,6 +18452,19 @@ func NewPostV0CityByCityNameSessionByIdStopRequest(server string, cityName strin return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -13409,18 +18532,18 @@ func NewStreamSessionRequest(server string, cityName string, id string, params * } // NewSubmitSessionRequest calls the generic SubmitSession builder with application/json body -func NewSubmitSessionRequest(server string, cityName string, id string, body SubmitSessionJSONRequestBody) (*http.Request, error) { +func NewSubmitSessionRequest(server string, cityName string, id string, params *SubmitSessionParams, body SubmitSessionJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewSubmitSessionRequestWithBody(server, cityName, id, "application/json", bodyReader) + return NewSubmitSessionRequestWithBody(server, cityName, id, params, "application/json", bodyReader) } // NewSubmitSessionRequestWithBody generates requests for SubmitSession with any type of body -func NewSubmitSessionRequestWithBody(server string, cityName string, id string, contentType string, body io.Reader) (*http.Request, error) { +func NewSubmitSessionRequestWithBody(server string, cityName string, id string, params *SubmitSessionParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -13459,11 +18582,24 @@ func NewSubmitSessionRequestWithBody(server string, cityName string, id string, req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameSessionByIdSuspendRequest generates requests for PostV0CityByCityNameSessionByIdSuspend -func NewPostV0CityByCityNameSessionByIdSuspendRequest(server string, cityName string, id string) (*http.Request, error) { +func NewPostV0CityByCityNameSessionByIdSuspendRequest(server string, cityName string, id string, params *PostV0CityByCityNameSessionByIdSuspendParams) (*http.Request, error) { var err error var pathParam0 string @@ -13500,6 +18636,19 @@ func NewPostV0CityByCityNameSessionByIdSuspendRequest(server string, cityName st return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -13599,7 +18748,7 @@ func NewGetV0CityByCityNameSessionByIdTranscriptRequest(server string, cityName } // NewPostV0CityByCityNameSessionByIdWakeRequest generates requests for PostV0CityByCityNameSessionByIdWake -func NewPostV0CityByCityNameSessionByIdWakeRequest(server string, cityName string, id string) (*http.Request, error) { +func NewPostV0CityByCityNameSessionByIdWakeRequest(server string, cityName string, id string, params *PostV0CityByCityNameSessionByIdWakeParams) (*http.Request, error) { var err error var pathParam0 string @@ -13636,6 +18785,19 @@ func NewPostV0CityByCityNameSessionByIdWakeRequest(server string, cityName strin return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -13760,18 +18922,18 @@ func NewGetV0CityByCityNameSessionsRequest(server string, cityName string, param } // NewCreateSessionRequest calls the generic CreateSession builder with application/json body -func NewCreateSessionRequest(server string, cityName string, body CreateSessionJSONRequestBody) (*http.Request, error) { +func NewCreateSessionRequest(server string, cityName string, params *CreateSessionParams, body CreateSessionJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewCreateSessionRequestWithBody(server, cityName, "application/json", bodyReader) + return NewCreateSessionRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewCreateSessionRequestWithBody generates requests for CreateSession with any type of body -func NewCreateSessionRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewCreateSessionRequestWithBody(server string, cityName string, params *CreateSessionParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -13803,22 +18965,35 @@ func NewCreateSessionRequestWithBody(server string, cityName string, contentType req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } // NewPostV0CityByCityNameSlingRequest calls the generic PostV0CityByCityNameSling builder with application/json body -func NewPostV0CityByCityNameSlingRequest(server string, cityName string, body PostV0CityByCityNameSlingJSONRequestBody) (*http.Request, error) { +func NewPostV0CityByCityNameSlingRequest(server string, cityName string, params *PostV0CityByCityNameSlingParams, body PostV0CityByCityNameSlingJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPostV0CityByCityNameSlingRequestWithBody(server, cityName, "application/json", bodyReader) + return NewPostV0CityByCityNameSlingRequestWithBody(server, cityName, params, "application/json", bodyReader) } // NewPostV0CityByCityNameSlingRequestWithBody generates requests for PostV0CityByCityNameSling with any type of body -func NewPostV0CityByCityNameSlingRequestWithBody(server string, cityName string, contentType string, body io.Reader) (*http.Request, error) { +func NewPostV0CityByCityNameSlingRequestWithBody(server string, cityName string, params *PostV0CityByCityNameSlingParams, contentType string, body io.Reader) (*http.Request, error) { var err error var pathParam0 string @@ -13850,6 +19025,19 @@ func NewPostV0CityByCityNameSlingRequestWithBody(server string, cityName string, req.Header.Add("Content-Type", contentType) + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -13925,6 +19113,53 @@ func NewGetV0CityByCityNameStatusRequest(server string, cityName string, params return req, nil } +// NewPostV0CityByCityNameUnregisterRequest generates requests for PostV0CityByCityNameUnregister +func NewPostV0CityByCityNameUnregisterRequest(server string, cityName string, params *PostV0CityByCityNameUnregisterParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "cityName", cityName, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/v0/city/%s/unregister", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), nil) + if err != nil { + return nil, err + } + + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + + return req, nil +} + // NewDeleteV0CityByCityNameWorkflowByWorkflowIdRequest generates requests for DeleteV0CityByCityNameWorkflowByWorkflowId func NewDeleteV0CityByCityNameWorkflowByWorkflowIdRequest(server string, cityName string, workflowId string, params *DeleteV0CityByCityNameWorkflowByWorkflowIdParams) (*http.Request, error) { var err error @@ -14017,6 +19252,19 @@ func NewDeleteV0CityByCityNameWorkflowByWorkflowIdRequest(server string, cityNam return nil, err } + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + return req, nil } @@ -14440,28 +19688,28 @@ type ClientWithResponsesInterface interface { GetV0CitiesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetV0CitiesResponse, error) // PostV0CityWithBodyWithResponse request with any body - PostV0CityWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityResponse, error) + PostV0CityWithBodyWithResponse(ctx context.Context, params *PostV0CityParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityResponse, error) - PostV0CityWithResponse(ctx context.Context, body PostV0CityJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityResponse, error) + PostV0CityWithResponse(ctx context.Context, params *PostV0CityParams, body PostV0CityJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityResponse, error) // GetV0CityByCityNameWithResponse request GetV0CityByCityNameWithResponse(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameResponse, error) // PatchV0CityByCityNameWithBodyWithResponse request with any body - PatchV0CityByCityNameWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameResponse, error) + PatchV0CityByCityNameWithBodyWithResponse(ctx context.Context, cityName string, params *PatchV0CityByCityNameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameResponse, error) - PatchV0CityByCityNameWithResponse(ctx context.Context, cityName string, body PatchV0CityByCityNameJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameResponse, error) + PatchV0CityByCityNameWithResponse(ctx context.Context, cityName string, params *PatchV0CityByCityNameParams, body PatchV0CityByCityNameJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameResponse, error) // DeleteV0CityByCityNameAgentByBaseWithResponse request - DeleteV0CityByCityNameAgentByBaseWithResponse(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameAgentByBaseResponse, error) + DeleteV0CityByCityNameAgentByBaseWithResponse(ctx context.Context, cityName string, base string, params *DeleteV0CityByCityNameAgentByBaseParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameAgentByBaseResponse, error) // GetV0CityByCityNameAgentByBaseWithResponse request GetV0CityByCityNameAgentByBaseWithResponse(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameAgentByBaseResponse, error) // PatchV0CityByCityNameAgentByBaseWithBodyWithResponse request with any body - PatchV0CityByCityNameAgentByBaseWithBodyWithResponse(ctx context.Context, cityName string, base string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByBaseResponse, error) + PatchV0CityByCityNameAgentByBaseWithBodyWithResponse(ctx context.Context, cityName string, base string, params *PatchV0CityByCityNameAgentByBaseParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByBaseResponse, error) - PatchV0CityByCityNameAgentByBaseWithResponse(ctx context.Context, cityName string, base string, body PatchV0CityByCityNameAgentByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByBaseResponse, error) + PatchV0CityByCityNameAgentByBaseWithResponse(ctx context.Context, cityName string, base string, params *PatchV0CityByCityNameAgentByBaseParams, body PatchV0CityByCityNameAgentByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByBaseResponse, error) // GetV0CityByCityNameAgentByBaseOutputWithResponse request GetV0CityByCityNameAgentByBaseOutputWithResponse(ctx context.Context, cityName string, base string, params *GetV0CityByCityNameAgentByBaseOutputParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameAgentByBaseOutputResponse, error) @@ -14470,18 +19718,18 @@ type ClientWithResponsesInterface interface { StreamAgentOutputWithResponse(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*StreamAgentOutputResponse, error) // PostV0CityByCityNameAgentByBaseByActionWithResponse request - PostV0CityByCityNameAgentByBaseByActionWithResponse(ctx context.Context, cityName string, base string, action PostV0CityByCityNameAgentByBaseByActionParamsAction, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameAgentByBaseByActionResponse, error) + PostV0CityByCityNameAgentByBaseByActionWithResponse(ctx context.Context, cityName string, base string, action PostV0CityByCityNameAgentByBaseByActionParamsAction, params *PostV0CityByCityNameAgentByBaseByActionParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameAgentByBaseByActionResponse, error) // DeleteV0CityByCityNameAgentByDirByBaseWithResponse request - DeleteV0CityByCityNameAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameAgentByDirByBaseResponse, error) + DeleteV0CityByCityNameAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, params *DeleteV0CityByCityNameAgentByDirByBaseParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameAgentByDirByBaseResponse, error) // GetV0CityByCityNameAgentByDirByBaseWithResponse request GetV0CityByCityNameAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameAgentByDirByBaseResponse, error) // PatchV0CityByCityNameAgentByDirByBaseWithBodyWithResponse request with any body - PatchV0CityByCityNameAgentByDirByBaseWithBodyWithResponse(ctx context.Context, cityName string, dir string, base string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByDirByBaseResponse, error) + PatchV0CityByCityNameAgentByDirByBaseWithBodyWithResponse(ctx context.Context, cityName string, dir string, base string, params *PatchV0CityByCityNameAgentByDirByBaseParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByDirByBaseResponse, error) - PatchV0CityByCityNameAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, body PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByDirByBaseResponse, error) + PatchV0CityByCityNameAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, params *PatchV0CityByCityNameAgentByDirByBaseParams, body PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByDirByBaseResponse, error) // GetV0CityByCityNameAgentByDirByBaseOutputWithResponse request GetV0CityByCityNameAgentByDirByBaseOutputWithResponse(ctx context.Context, cityName string, dir string, base string, params *GetV0CityByCityNameAgentByDirByBaseOutputParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameAgentByDirByBaseOutputResponse, error) @@ -14490,45 +19738,45 @@ type ClientWithResponsesInterface interface { StreamAgentOutputQualifiedWithResponse(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*StreamAgentOutputQualifiedResponse, error) // PostV0CityByCityNameAgentByDirByBaseByActionWithResponse request - PostV0CityByCityNameAgentByDirByBaseByActionWithResponse(ctx context.Context, cityName string, dir string, base string, action PostV0CityByCityNameAgentByDirByBaseByActionParamsAction, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameAgentByDirByBaseByActionResponse, error) + PostV0CityByCityNameAgentByDirByBaseByActionWithResponse(ctx context.Context, cityName string, dir string, base string, action PostV0CityByCityNameAgentByDirByBaseByActionParamsAction, params *PostV0CityByCityNameAgentByDirByBaseByActionParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameAgentByDirByBaseByActionResponse, error) // GetV0CityByCityNameAgentsWithResponse request GetV0CityByCityNameAgentsWithResponse(ctx context.Context, cityName string, params *GetV0CityByCityNameAgentsParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameAgentsResponse, error) // CreateAgentWithBodyWithResponse request with any body - CreateAgentWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateAgentResponse, error) + CreateAgentWithBodyWithResponse(ctx context.Context, cityName string, params *CreateAgentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateAgentResponse, error) - CreateAgentWithResponse(ctx context.Context, cityName string, body CreateAgentJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateAgentResponse, error) + CreateAgentWithResponse(ctx context.Context, cityName string, params *CreateAgentParams, body CreateAgentJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateAgentResponse, error) // DeleteV0CityByCityNameBeadByIdWithResponse request - DeleteV0CityByCityNameBeadByIdWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameBeadByIdResponse, error) + DeleteV0CityByCityNameBeadByIdWithResponse(ctx context.Context, cityName string, id string, params *DeleteV0CityByCityNameBeadByIdParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameBeadByIdResponse, error) // GetV0CityByCityNameBeadByIdWithResponse request GetV0CityByCityNameBeadByIdWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameBeadByIdResponse, error) // PatchV0CityByCityNameBeadByIdWithBodyWithResponse request with any body - PatchV0CityByCityNameBeadByIdWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameBeadByIdResponse, error) + PatchV0CityByCityNameBeadByIdWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameBeadByIdParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameBeadByIdResponse, error) - PatchV0CityByCityNameBeadByIdWithResponse(ctx context.Context, cityName string, id string, body PatchV0CityByCityNameBeadByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameBeadByIdResponse, error) + PatchV0CityByCityNameBeadByIdWithResponse(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameBeadByIdParams, body PatchV0CityByCityNameBeadByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameBeadByIdResponse, error) // PostV0CityByCityNameBeadByIdAssignWithBodyWithResponse request with any body - PostV0CityByCityNameBeadByIdAssignWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdAssignResponse, error) + PostV0CityByCityNameBeadByIdAssignWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdAssignParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdAssignResponse, error) - PostV0CityByCityNameBeadByIdAssignWithResponse(ctx context.Context, cityName string, id string, body PostV0CityByCityNameBeadByIdAssignJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdAssignResponse, error) + PostV0CityByCityNameBeadByIdAssignWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdAssignParams, body PostV0CityByCityNameBeadByIdAssignJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdAssignResponse, error) // PostV0CityByCityNameBeadByIdCloseWithResponse request - PostV0CityByCityNameBeadByIdCloseWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdCloseResponse, error) + PostV0CityByCityNameBeadByIdCloseWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdCloseParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdCloseResponse, error) // GetV0CityByCityNameBeadByIdDepsWithResponse request GetV0CityByCityNameBeadByIdDepsWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameBeadByIdDepsResponse, error) // PostV0CityByCityNameBeadByIdReopenWithResponse request - PostV0CityByCityNameBeadByIdReopenWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdReopenResponse, error) + PostV0CityByCityNameBeadByIdReopenWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdReopenParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdReopenResponse, error) // PostV0CityByCityNameBeadByIdUpdateWithBodyWithResponse request with any body - PostV0CityByCityNameBeadByIdUpdateWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdUpdateResponse, error) + PostV0CityByCityNameBeadByIdUpdateWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdUpdateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdUpdateResponse, error) - PostV0CityByCityNameBeadByIdUpdateWithResponse(ctx context.Context, cityName string, id string, body PostV0CityByCityNameBeadByIdUpdateJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdUpdateResponse, error) + PostV0CityByCityNameBeadByIdUpdateWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdUpdateParams, body PostV0CityByCityNameBeadByIdUpdateJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdUpdateResponse, error) // GetV0CityByCityNameBeadsWithResponse request GetV0CityByCityNameBeadsWithResponse(ctx context.Context, cityName string, params *GetV0CityByCityNameBeadsParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameBeadsResponse, error) @@ -14554,63 +19802,63 @@ type ClientWithResponsesInterface interface { GetV0CityByCityNameConfigValidateWithResponse(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameConfigValidateResponse, error) // DeleteV0CityByCityNameConvoyByIdWithResponse request - DeleteV0CityByCityNameConvoyByIdWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameConvoyByIdResponse, error) + DeleteV0CityByCityNameConvoyByIdWithResponse(ctx context.Context, cityName string, id string, params *DeleteV0CityByCityNameConvoyByIdParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameConvoyByIdResponse, error) // GetV0CityByCityNameConvoyByIdWithResponse request GetV0CityByCityNameConvoyByIdWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameConvoyByIdResponse, error) // PostV0CityByCityNameConvoyByIdAddWithBodyWithResponse request with any body - PostV0CityByCityNameConvoyByIdAddWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdAddResponse, error) + PostV0CityByCityNameConvoyByIdAddWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdAddParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdAddResponse, error) - PostV0CityByCityNameConvoyByIdAddWithResponse(ctx context.Context, cityName string, id string, body PostV0CityByCityNameConvoyByIdAddJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdAddResponse, error) + PostV0CityByCityNameConvoyByIdAddWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdAddParams, body PostV0CityByCityNameConvoyByIdAddJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdAddResponse, error) // GetV0CityByCityNameConvoyByIdCheckWithResponse request GetV0CityByCityNameConvoyByIdCheckWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameConvoyByIdCheckResponse, error) // PostV0CityByCityNameConvoyByIdCloseWithResponse request - PostV0CityByCityNameConvoyByIdCloseWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdCloseResponse, error) + PostV0CityByCityNameConvoyByIdCloseWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdCloseParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdCloseResponse, error) // PostV0CityByCityNameConvoyByIdRemoveWithBodyWithResponse request with any body - PostV0CityByCityNameConvoyByIdRemoveWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdRemoveResponse, error) + PostV0CityByCityNameConvoyByIdRemoveWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdRemoveParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdRemoveResponse, error) - PostV0CityByCityNameConvoyByIdRemoveWithResponse(ctx context.Context, cityName string, id string, body PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdRemoveResponse, error) + PostV0CityByCityNameConvoyByIdRemoveWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdRemoveParams, body PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdRemoveResponse, error) // GetV0CityByCityNameConvoysWithResponse request GetV0CityByCityNameConvoysWithResponse(ctx context.Context, cityName string, params *GetV0CityByCityNameConvoysParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameConvoysResponse, error) // CreateConvoyWithBodyWithResponse request with any body - CreateConvoyWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateConvoyResponse, error) + CreateConvoyWithBodyWithResponse(ctx context.Context, cityName string, params *CreateConvoyParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateConvoyResponse, error) - CreateConvoyWithResponse(ctx context.Context, cityName string, body CreateConvoyJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateConvoyResponse, error) + CreateConvoyWithResponse(ctx context.Context, cityName string, params *CreateConvoyParams, body CreateConvoyJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateConvoyResponse, error) // GetV0CityByCityNameEventsWithResponse request GetV0CityByCityNameEventsWithResponse(ctx context.Context, cityName string, params *GetV0CityByCityNameEventsParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameEventsResponse, error) // EmitEventWithBodyWithResponse request with any body - EmitEventWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*EmitEventResponse, error) + EmitEventWithBodyWithResponse(ctx context.Context, cityName string, params *EmitEventParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*EmitEventResponse, error) - EmitEventWithResponse(ctx context.Context, cityName string, body EmitEventJSONRequestBody, reqEditors ...RequestEditorFn) (*EmitEventResponse, error) + EmitEventWithResponse(ctx context.Context, cityName string, params *EmitEventParams, body EmitEventJSONRequestBody, reqEditors ...RequestEditorFn) (*EmitEventResponse, error) // StreamEventsWithResponse request StreamEventsWithResponse(ctx context.Context, cityName string, params *StreamEventsParams, reqEditors ...RequestEditorFn) (*StreamEventsResponse, error) // DeleteV0CityByCityNameExtmsgAdaptersWithBodyWithResponse request with any body - DeleteV0CityByCityNameExtmsgAdaptersWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgAdaptersResponse, error) + DeleteV0CityByCityNameExtmsgAdaptersWithBodyWithResponse(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgAdaptersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgAdaptersResponse, error) - DeleteV0CityByCityNameExtmsgAdaptersWithResponse(ctx context.Context, cityName string, body DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgAdaptersResponse, error) + DeleteV0CityByCityNameExtmsgAdaptersWithResponse(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgAdaptersParams, body DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgAdaptersResponse, error) // GetV0CityByCityNameExtmsgAdaptersWithResponse request GetV0CityByCityNameExtmsgAdaptersWithResponse(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameExtmsgAdaptersResponse, error) // RegisterExtmsgAdapterWithBodyWithResponse request with any body - RegisterExtmsgAdapterWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RegisterExtmsgAdapterResponse, error) + RegisterExtmsgAdapterWithBodyWithResponse(ctx context.Context, cityName string, params *RegisterExtmsgAdapterParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RegisterExtmsgAdapterResponse, error) - RegisterExtmsgAdapterWithResponse(ctx context.Context, cityName string, body RegisterExtmsgAdapterJSONRequestBody, reqEditors ...RequestEditorFn) (*RegisterExtmsgAdapterResponse, error) + RegisterExtmsgAdapterWithResponse(ctx context.Context, cityName string, params *RegisterExtmsgAdapterParams, body RegisterExtmsgAdapterJSONRequestBody, reqEditors ...RequestEditorFn) (*RegisterExtmsgAdapterResponse, error) // PostV0CityByCityNameExtmsgBindWithBodyWithResponse request with any body - PostV0CityByCityNameExtmsgBindWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgBindResponse, error) + PostV0CityByCityNameExtmsgBindWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgBindParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgBindResponse, error) - PostV0CityByCityNameExtmsgBindWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgBindJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgBindResponse, error) + PostV0CityByCityNameExtmsgBindWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgBindParams, body PostV0CityByCityNameExtmsgBindJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgBindResponse, error) // GetV0CityByCityNameExtmsgBindingsWithResponse request GetV0CityByCityNameExtmsgBindingsWithResponse(ctx context.Context, cityName string, params *GetV0CityByCityNameExtmsgBindingsParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameExtmsgBindingsResponse, error) @@ -14619,42 +19867,42 @@ type ClientWithResponsesInterface interface { GetV0CityByCityNameExtmsgGroupsWithResponse(ctx context.Context, cityName string, params *GetV0CityByCityNameExtmsgGroupsParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameExtmsgGroupsResponse, error) // EnsureExtmsgGroupWithBodyWithResponse request with any body - EnsureExtmsgGroupWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*EnsureExtmsgGroupResponse, error) + EnsureExtmsgGroupWithBodyWithResponse(ctx context.Context, cityName string, params *EnsureExtmsgGroupParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*EnsureExtmsgGroupResponse, error) - EnsureExtmsgGroupWithResponse(ctx context.Context, cityName string, body EnsureExtmsgGroupJSONRequestBody, reqEditors ...RequestEditorFn) (*EnsureExtmsgGroupResponse, error) + EnsureExtmsgGroupWithResponse(ctx context.Context, cityName string, params *EnsureExtmsgGroupParams, body EnsureExtmsgGroupJSONRequestBody, reqEditors ...RequestEditorFn) (*EnsureExtmsgGroupResponse, error) // PostV0CityByCityNameExtmsgInboundWithBodyWithResponse request with any body - PostV0CityByCityNameExtmsgInboundWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgInboundResponse, error) + PostV0CityByCityNameExtmsgInboundWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgInboundParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgInboundResponse, error) - PostV0CityByCityNameExtmsgInboundWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgInboundJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgInboundResponse, error) + PostV0CityByCityNameExtmsgInboundWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgInboundParams, body PostV0CityByCityNameExtmsgInboundJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgInboundResponse, error) // PostV0CityByCityNameExtmsgOutboundWithBodyWithResponse request with any body - PostV0CityByCityNameExtmsgOutboundWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgOutboundResponse, error) + PostV0CityByCityNameExtmsgOutboundWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgOutboundParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgOutboundResponse, error) - PostV0CityByCityNameExtmsgOutboundWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgOutboundJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgOutboundResponse, error) + PostV0CityByCityNameExtmsgOutboundWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgOutboundParams, body PostV0CityByCityNameExtmsgOutboundJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgOutboundResponse, error) // DeleteV0CityByCityNameExtmsgParticipantsWithBodyWithResponse request with any body - DeleteV0CityByCityNameExtmsgParticipantsWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgParticipantsResponse, error) + DeleteV0CityByCityNameExtmsgParticipantsWithBodyWithResponse(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgParticipantsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgParticipantsResponse, error) - DeleteV0CityByCityNameExtmsgParticipantsWithResponse(ctx context.Context, cityName string, body DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgParticipantsResponse, error) + DeleteV0CityByCityNameExtmsgParticipantsWithResponse(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgParticipantsParams, body DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgParticipantsResponse, error) // PostV0CityByCityNameExtmsgParticipantsWithBodyWithResponse request with any body - PostV0CityByCityNameExtmsgParticipantsWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgParticipantsResponse, error) + PostV0CityByCityNameExtmsgParticipantsWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgParticipantsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgParticipantsResponse, error) - PostV0CityByCityNameExtmsgParticipantsWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgParticipantsResponse, error) + PostV0CityByCityNameExtmsgParticipantsWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgParticipantsParams, body PostV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgParticipantsResponse, error) // GetV0CityByCityNameExtmsgTranscriptWithResponse request GetV0CityByCityNameExtmsgTranscriptWithResponse(ctx context.Context, cityName string, params *GetV0CityByCityNameExtmsgTranscriptParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameExtmsgTranscriptResponse, error) // PostV0CityByCityNameExtmsgTranscriptAckWithBodyWithResponse request with any body - PostV0CityByCityNameExtmsgTranscriptAckWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgTranscriptAckResponse, error) + PostV0CityByCityNameExtmsgTranscriptAckWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgTranscriptAckParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgTranscriptAckResponse, error) - PostV0CityByCityNameExtmsgTranscriptAckWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgTranscriptAckResponse, error) + PostV0CityByCityNameExtmsgTranscriptAckWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgTranscriptAckParams, body PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgTranscriptAckResponse, error) // PostV0CityByCityNameExtmsgUnbindWithBodyWithResponse request with any body - PostV0CityByCityNameExtmsgUnbindWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgUnbindResponse, error) + PostV0CityByCityNameExtmsgUnbindWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgUnbindParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgUnbindResponse, error) - PostV0CityByCityNameExtmsgUnbindWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgUnbindJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgUnbindResponse, error) + PostV0CityByCityNameExtmsgUnbindWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgUnbindParams, body PostV0CityByCityNameExtmsgUnbindJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgUnbindResponse, error) // GetV0CityByCityNameFormulaByNameWithResponse request GetV0CityByCityNameFormulaByNameWithResponse(ctx context.Context, cityName string, name string, params *GetV0CityByCityNameFormulaByNameParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameFormulaByNameResponse, error) @@ -14669,9 +19917,9 @@ type ClientWithResponsesInterface interface { GetV0CityByCityNameFormulasByNameWithResponse(ctx context.Context, cityName string, name string, params *GetV0CityByCityNameFormulasByNameParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameFormulasByNameResponse, error) // PostV0CityByCityNameFormulasByNamePreviewWithBodyWithResponse request with any body - PostV0CityByCityNameFormulasByNamePreviewWithBodyWithResponse(ctx context.Context, cityName string, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameFormulasByNamePreviewResponse, error) + PostV0CityByCityNameFormulasByNamePreviewWithBodyWithResponse(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameFormulasByNamePreviewParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameFormulasByNamePreviewResponse, error) - PostV0CityByCityNameFormulasByNamePreviewWithResponse(ctx context.Context, cityName string, name string, body PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameFormulasByNamePreviewResponse, error) + PostV0CityByCityNameFormulasByNamePreviewWithResponse(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameFormulasByNamePreviewParams, body PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameFormulasByNamePreviewResponse, error) // GetV0CityByCityNameFormulasByNameRunsWithResponse request GetV0CityByCityNameFormulasByNameRunsWithResponse(ctx context.Context, cityName string, name string, params *GetV0CityByCityNameFormulasByNameRunsParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameFormulasByNameRunsResponse, error) @@ -14720,10 +19968,10 @@ type ClientWithResponsesInterface interface { GetV0CityByCityNameOrderByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameOrderByNameResponse, error) // PostV0CityByCityNameOrderByNameDisableWithResponse request - PostV0CityByCityNameOrderByNameDisableWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameOrderByNameDisableResponse, error) + PostV0CityByCityNameOrderByNameDisableWithResponse(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameOrderByNameDisableParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameOrderByNameDisableResponse, error) // PostV0CityByCityNameOrderByNameEnableWithResponse request - PostV0CityByCityNameOrderByNameEnableWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameOrderByNameEnableResponse, error) + PostV0CityByCityNameOrderByNameEnableWithResponse(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameOrderByNameEnableParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameOrderByNameEnableResponse, error) // GetV0CityByCityNameOrdersWithResponse request GetV0CityByCityNameOrdersWithResponse(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameOrdersResponse, error) @@ -14741,13 +19989,13 @@ type ClientWithResponsesInterface interface { GetV0CityByCityNamePacksWithResponse(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNamePacksResponse, error) // DeleteV0CityByCityNamePatchesAgentByBaseWithResponse request - DeleteV0CityByCityNamePatchesAgentByBaseWithResponse(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesAgentByBaseResponse, error) + DeleteV0CityByCityNamePatchesAgentByBaseWithResponse(ctx context.Context, cityName string, base string, params *DeleteV0CityByCityNamePatchesAgentByBaseParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesAgentByBaseResponse, error) // GetV0CityByCityNamePatchesAgentByBaseWithResponse request GetV0CityByCityNamePatchesAgentByBaseWithResponse(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNamePatchesAgentByBaseResponse, error) // DeleteV0CityByCityNamePatchesAgentByDirByBaseWithResponse request - DeleteV0CityByCityNamePatchesAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesAgentByDirByBaseResponse, error) + DeleteV0CityByCityNamePatchesAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, params *DeleteV0CityByCityNamePatchesAgentByDirByBaseParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesAgentByDirByBaseResponse, error) // GetV0CityByCityNamePatchesAgentByDirByBaseWithResponse request GetV0CityByCityNamePatchesAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNamePatchesAgentByDirByBaseResponse, error) @@ -14756,12 +20004,12 @@ type ClientWithResponsesInterface interface { GetV0CityByCityNamePatchesAgentsWithResponse(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNamePatchesAgentsResponse, error) // PutV0CityByCityNamePatchesAgentsWithBodyWithResponse request with any body - PutV0CityByCityNamePatchesAgentsWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesAgentsResponse, error) + PutV0CityByCityNamePatchesAgentsWithBodyWithResponse(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesAgentsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesAgentsResponse, error) - PutV0CityByCityNamePatchesAgentsWithResponse(ctx context.Context, cityName string, body PutV0CityByCityNamePatchesAgentsJSONRequestBody, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesAgentsResponse, error) + PutV0CityByCityNamePatchesAgentsWithResponse(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesAgentsParams, body PutV0CityByCityNamePatchesAgentsJSONRequestBody, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesAgentsResponse, error) // DeleteV0CityByCityNamePatchesProviderByNameWithResponse request - DeleteV0CityByCityNamePatchesProviderByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesProviderByNameResponse, error) + DeleteV0CityByCityNamePatchesProviderByNameWithResponse(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNamePatchesProviderByNameParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesProviderByNameResponse, error) // GetV0CityByCityNamePatchesProviderByNameWithResponse request GetV0CityByCityNamePatchesProviderByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNamePatchesProviderByNameResponse, error) @@ -14770,12 +20018,12 @@ type ClientWithResponsesInterface interface { GetV0CityByCityNamePatchesProvidersWithResponse(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNamePatchesProvidersResponse, error) // PutV0CityByCityNamePatchesProvidersWithBodyWithResponse request with any body - PutV0CityByCityNamePatchesProvidersWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesProvidersResponse, error) + PutV0CityByCityNamePatchesProvidersWithBodyWithResponse(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesProvidersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesProvidersResponse, error) - PutV0CityByCityNamePatchesProvidersWithResponse(ctx context.Context, cityName string, body PutV0CityByCityNamePatchesProvidersJSONRequestBody, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesProvidersResponse, error) + PutV0CityByCityNamePatchesProvidersWithResponse(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesProvidersParams, body PutV0CityByCityNamePatchesProvidersJSONRequestBody, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesProvidersResponse, error) // DeleteV0CityByCityNamePatchesRigByNameWithResponse request - DeleteV0CityByCityNamePatchesRigByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesRigByNameResponse, error) + DeleteV0CityByCityNamePatchesRigByNameWithResponse(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNamePatchesRigByNameParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesRigByNameResponse, error) // GetV0CityByCityNamePatchesRigByNameWithResponse request GetV0CityByCityNamePatchesRigByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNamePatchesRigByNameResponse, error) @@ -14784,31 +20032,31 @@ type ClientWithResponsesInterface interface { GetV0CityByCityNamePatchesRigsWithResponse(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNamePatchesRigsResponse, error) // PutV0CityByCityNamePatchesRigsWithBodyWithResponse request with any body - PutV0CityByCityNamePatchesRigsWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesRigsResponse, error) + PutV0CityByCityNamePatchesRigsWithBodyWithResponse(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesRigsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesRigsResponse, error) - PutV0CityByCityNamePatchesRigsWithResponse(ctx context.Context, cityName string, body PutV0CityByCityNamePatchesRigsJSONRequestBody, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesRigsResponse, error) + PutV0CityByCityNamePatchesRigsWithResponse(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesRigsParams, body PutV0CityByCityNamePatchesRigsJSONRequestBody, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesRigsResponse, error) // GetV0CityByCityNameProviderReadinessWithResponse request GetV0CityByCityNameProviderReadinessWithResponse(ctx context.Context, cityName string, params *GetV0CityByCityNameProviderReadinessParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameProviderReadinessResponse, error) // DeleteV0CityByCityNameProviderByNameWithResponse request - DeleteV0CityByCityNameProviderByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameProviderByNameResponse, error) + DeleteV0CityByCityNameProviderByNameWithResponse(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNameProviderByNameParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameProviderByNameResponse, error) // GetV0CityByCityNameProviderByNameWithResponse request GetV0CityByCityNameProviderByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameProviderByNameResponse, error) // PatchV0CityByCityNameProviderByNameWithBodyWithResponse request with any body - PatchV0CityByCityNameProviderByNameWithBodyWithResponse(ctx context.Context, cityName string, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameProviderByNameResponse, error) + PatchV0CityByCityNameProviderByNameWithBodyWithResponse(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameProviderByNameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameProviderByNameResponse, error) - PatchV0CityByCityNameProviderByNameWithResponse(ctx context.Context, cityName string, name string, body PatchV0CityByCityNameProviderByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameProviderByNameResponse, error) + PatchV0CityByCityNameProviderByNameWithResponse(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameProviderByNameParams, body PatchV0CityByCityNameProviderByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameProviderByNameResponse, error) // GetV0CityByCityNameProvidersWithResponse request GetV0CityByCityNameProvidersWithResponse(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameProvidersResponse, error) // CreateProviderWithBodyWithResponse request with any body - CreateProviderWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateProviderResponse, error) + CreateProviderWithBodyWithResponse(ctx context.Context, cityName string, params *CreateProviderParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateProviderResponse, error) - CreateProviderWithResponse(ctx context.Context, cityName string, body CreateProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateProviderResponse, error) + CreateProviderWithResponse(ctx context.Context, cityName string, params *CreateProviderParams, body CreateProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateProviderResponse, error) // GetV0CityByCityNameProvidersPublicWithResponse request GetV0CityByCityNameProvidersPublicWithResponse(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameProvidersPublicResponse, error) @@ -14817,32 +20065,32 @@ type ClientWithResponsesInterface interface { GetV0CityByCityNameReadinessWithResponse(ctx context.Context, cityName string, params *GetV0CityByCityNameReadinessParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameReadinessResponse, error) // DeleteV0CityByCityNameRigByNameWithResponse request - DeleteV0CityByCityNameRigByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameRigByNameResponse, error) + DeleteV0CityByCityNameRigByNameWithResponse(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNameRigByNameParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameRigByNameResponse, error) // GetV0CityByCityNameRigByNameWithResponse request GetV0CityByCityNameRigByNameWithResponse(ctx context.Context, cityName string, name string, params *GetV0CityByCityNameRigByNameParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameRigByNameResponse, error) // PatchV0CityByCityNameRigByNameWithBodyWithResponse request with any body - PatchV0CityByCityNameRigByNameWithBodyWithResponse(ctx context.Context, cityName string, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameRigByNameResponse, error) + PatchV0CityByCityNameRigByNameWithBodyWithResponse(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameRigByNameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameRigByNameResponse, error) - PatchV0CityByCityNameRigByNameWithResponse(ctx context.Context, cityName string, name string, body PatchV0CityByCityNameRigByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameRigByNameResponse, error) + PatchV0CityByCityNameRigByNameWithResponse(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameRigByNameParams, body PatchV0CityByCityNameRigByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameRigByNameResponse, error) // PostV0CityByCityNameRigByNameByActionWithResponse request - PostV0CityByCityNameRigByNameByActionWithResponse(ctx context.Context, cityName string, name string, action string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameRigByNameByActionResponse, error) + PostV0CityByCityNameRigByNameByActionWithResponse(ctx context.Context, cityName string, name string, action string, params *PostV0CityByCityNameRigByNameByActionParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameRigByNameByActionResponse, error) // GetV0CityByCityNameRigsWithResponse request GetV0CityByCityNameRigsWithResponse(ctx context.Context, cityName string, params *GetV0CityByCityNameRigsParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameRigsResponse, error) // CreateRigWithBodyWithResponse request with any body - CreateRigWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateRigResponse, error) + CreateRigWithBodyWithResponse(ctx context.Context, cityName string, params *CreateRigParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateRigResponse, error) - CreateRigWithResponse(ctx context.Context, cityName string, body CreateRigJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateRigResponse, error) + CreateRigWithResponse(ctx context.Context, cityName string, params *CreateRigParams, body CreateRigJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateRigResponse, error) // GetV0CityByCityNameServiceByNameWithResponse request GetV0CityByCityNameServiceByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameServiceByNameResponse, error) // PostV0CityByCityNameServiceByNameRestartWithResponse request - PostV0CityByCityNameServiceByNameRestartWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameServiceByNameRestartResponse, error) + PostV0CityByCityNameServiceByNameRestartWithResponse(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameServiceByNameRestartParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameServiceByNameRestartResponse, error) // GetV0CityByCityNameServicesWithResponse request GetV0CityByCityNameServicesWithResponse(ctx context.Context, cityName string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameServicesResponse, error) @@ -14851,9 +20099,9 @@ type ClientWithResponsesInterface interface { GetV0CityByCityNameSessionByIdWithResponse(ctx context.Context, cityName string, id string, params *GetV0CityByCityNameSessionByIdParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameSessionByIdResponse, error) // PatchV0CityByCityNameSessionByIdWithBodyWithResponse request with any body - PatchV0CityByCityNameSessionByIdWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameSessionByIdResponse, error) + PatchV0CityByCityNameSessionByIdWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameSessionByIdParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameSessionByIdResponse, error) - PatchV0CityByCityNameSessionByIdWithResponse(ctx context.Context, cityName string, id string, body PatchV0CityByCityNameSessionByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameSessionByIdResponse, error) + PatchV0CityByCityNameSessionByIdWithResponse(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameSessionByIdParams, body PatchV0CityByCityNameSessionByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameSessionByIdResponse, error) // GetV0CityByCityNameSessionByIdAgentsWithResponse request GetV0CityByCityNameSessionByIdAgentsWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameSessionByIdAgentsResponse, error) @@ -14865,62 +20113,65 @@ type ClientWithResponsesInterface interface { PostV0CityByCityNameSessionByIdCloseWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdCloseParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdCloseResponse, error) // PostV0CityByCityNameSessionByIdKillWithResponse request - PostV0CityByCityNameSessionByIdKillWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdKillResponse, error) + PostV0CityByCityNameSessionByIdKillWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdKillParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdKillResponse, error) // SendSessionMessageWithBodyWithResponse request with any body - SendSessionMessageWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SendSessionMessageResponse, error) + SendSessionMessageWithBodyWithResponse(ctx context.Context, cityName string, id string, params *SendSessionMessageParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SendSessionMessageResponse, error) - SendSessionMessageWithResponse(ctx context.Context, cityName string, id string, body SendSessionMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*SendSessionMessageResponse, error) + SendSessionMessageWithResponse(ctx context.Context, cityName string, id string, params *SendSessionMessageParams, body SendSessionMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*SendSessionMessageResponse, error) // GetV0CityByCityNameSessionByIdPendingWithResponse request GetV0CityByCityNameSessionByIdPendingWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameSessionByIdPendingResponse, error) // PostV0CityByCityNameSessionByIdRenameWithBodyWithResponse request with any body - PostV0CityByCityNameSessionByIdRenameWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdRenameResponse, error) + PostV0CityByCityNameSessionByIdRenameWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdRenameResponse, error) - PostV0CityByCityNameSessionByIdRenameWithResponse(ctx context.Context, cityName string, id string, body PostV0CityByCityNameSessionByIdRenameJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdRenameResponse, error) + PostV0CityByCityNameSessionByIdRenameWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, body PostV0CityByCityNameSessionByIdRenameJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdRenameResponse, error) // RespondSessionWithBodyWithResponse request with any body - RespondSessionWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RespondSessionResponse, error) + RespondSessionWithBodyWithResponse(ctx context.Context, cityName string, id string, params *RespondSessionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RespondSessionResponse, error) - RespondSessionWithResponse(ctx context.Context, cityName string, id string, body RespondSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*RespondSessionResponse, error) + RespondSessionWithResponse(ctx context.Context, cityName string, id string, params *RespondSessionParams, body RespondSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*RespondSessionResponse, error) // PostV0CityByCityNameSessionByIdStopWithResponse request - PostV0CityByCityNameSessionByIdStopWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdStopResponse, error) + PostV0CityByCityNameSessionByIdStopWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdStopParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdStopResponse, error) // StreamSessionWithResponse request StreamSessionWithResponse(ctx context.Context, cityName string, id string, params *StreamSessionParams, reqEditors ...RequestEditorFn) (*StreamSessionResponse, error) // SubmitSessionWithBodyWithResponse request with any body - SubmitSessionWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SubmitSessionResponse, error) + SubmitSessionWithBodyWithResponse(ctx context.Context, cityName string, id string, params *SubmitSessionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SubmitSessionResponse, error) - SubmitSessionWithResponse(ctx context.Context, cityName string, id string, body SubmitSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*SubmitSessionResponse, error) + SubmitSessionWithResponse(ctx context.Context, cityName string, id string, params *SubmitSessionParams, body SubmitSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*SubmitSessionResponse, error) // PostV0CityByCityNameSessionByIdSuspendWithResponse request - PostV0CityByCityNameSessionByIdSuspendWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdSuspendResponse, error) + PostV0CityByCityNameSessionByIdSuspendWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdSuspendParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdSuspendResponse, error) // GetV0CityByCityNameSessionByIdTranscriptWithResponse request GetV0CityByCityNameSessionByIdTranscriptWithResponse(ctx context.Context, cityName string, id string, params *GetV0CityByCityNameSessionByIdTranscriptParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameSessionByIdTranscriptResponse, error) // PostV0CityByCityNameSessionByIdWakeWithResponse request - PostV0CityByCityNameSessionByIdWakeWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdWakeResponse, error) + PostV0CityByCityNameSessionByIdWakeWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdWakeParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdWakeResponse, error) // GetV0CityByCityNameSessionsWithResponse request GetV0CityByCityNameSessionsWithResponse(ctx context.Context, cityName string, params *GetV0CityByCityNameSessionsParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameSessionsResponse, error) // CreateSessionWithBodyWithResponse request with any body - CreateSessionWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateSessionResponse, error) + CreateSessionWithBodyWithResponse(ctx context.Context, cityName string, params *CreateSessionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateSessionResponse, error) - CreateSessionWithResponse(ctx context.Context, cityName string, body CreateSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateSessionResponse, error) + CreateSessionWithResponse(ctx context.Context, cityName string, params *CreateSessionParams, body CreateSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateSessionResponse, error) // PostV0CityByCityNameSlingWithBodyWithResponse request with any body - PostV0CityByCityNameSlingWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSlingResponse, error) + PostV0CityByCityNameSlingWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameSlingParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSlingResponse, error) - PostV0CityByCityNameSlingWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameSlingJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSlingResponse, error) + PostV0CityByCityNameSlingWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameSlingParams, body PostV0CityByCityNameSlingJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSlingResponse, error) // GetV0CityByCityNameStatusWithResponse request GetV0CityByCityNameStatusWithResponse(ctx context.Context, cityName string, params *GetV0CityByCityNameStatusParams, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameStatusResponse, error) + // PostV0CityByCityNameUnregisterWithResponse request + PostV0CityByCityNameUnregisterWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameUnregisterParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameUnregisterResponse, error) + // DeleteV0CityByCityNameWorkflowByWorkflowIdWithResponse request DeleteV0CityByCityNameWorkflowByWorkflowIdWithResponse(ctx context.Context, cityName string, workflowId string, params *DeleteV0CityByCityNameWorkflowByWorkflowIdParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameWorkflowByWorkflowIdResponse, error) @@ -14989,7 +20240,7 @@ func (r GetV0CitiesResponse) StatusCode() int { type PostV0CityResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *CityCreateResponse + JSON202 *CityCreateResponse ApplicationproblemJSONDefault *ErrorModel } @@ -18064,6 +23315,29 @@ func (r GetV0CityByCityNameStatusResponse) StatusCode() int { return 0 } +type PostV0CityByCityNameUnregisterResponse struct { + Body []byte + HTTPResponse *http.Response + JSON202 *CityUnregisterResponse + ApplicationproblemJSONDefault *ErrorModel +} + +// Status returns HTTPResponse.Status +func (r PostV0CityByCityNameUnregisterResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostV0CityByCityNameUnregisterResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type DeleteV0CityByCityNameWorkflowByWorkflowIdResponse struct { Body []byte HTTPResponse *http.Response @@ -18220,16 +23494,16 @@ func (c *ClientWithResponses) GetV0CitiesWithResponse(ctx context.Context, reqEd } // PostV0CityWithBodyWithResponse request with arbitrary body returning *PostV0CityResponse -func (c *ClientWithResponses) PostV0CityWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityResponse, error) { - rsp, err := c.PostV0CityWithBody(ctx, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityWithBodyWithResponse(ctx context.Context, params *PostV0CityParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityResponse, error) { + rsp, err := c.PostV0CityWithBody(ctx, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityResponse(rsp) } -func (c *ClientWithResponses) PostV0CityWithResponse(ctx context.Context, body PostV0CityJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityResponse, error) { - rsp, err := c.PostV0City(ctx, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityWithResponse(ctx context.Context, params *PostV0CityParams, body PostV0CityJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityResponse, error) { + rsp, err := c.PostV0City(ctx, params, body, reqEditors...) if err != nil { return nil, err } @@ -18246,16 +23520,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameWithResponse(ctx context.Contex } // PatchV0CityByCityNameWithBodyWithResponse request with arbitrary body returning *PatchV0CityByCityNameResponse -func (c *ClientWithResponses) PatchV0CityByCityNameWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameResponse, error) { - rsp, err := c.PatchV0CityByCityNameWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameWithBodyWithResponse(ctx context.Context, cityName string, params *PatchV0CityByCityNameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameResponse, error) { + rsp, err := c.PatchV0CityByCityNameWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePatchV0CityByCityNameResponse(rsp) } -func (c *ClientWithResponses) PatchV0CityByCityNameWithResponse(ctx context.Context, cityName string, body PatchV0CityByCityNameJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameResponse, error) { - rsp, err := c.PatchV0CityByCityName(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameWithResponse(ctx context.Context, cityName string, params *PatchV0CityByCityNameParams, body PatchV0CityByCityNameJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameResponse, error) { + rsp, err := c.PatchV0CityByCityName(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18263,8 +23537,8 @@ func (c *ClientWithResponses) PatchV0CityByCityNameWithResponse(ctx context.Cont } // DeleteV0CityByCityNameAgentByBaseWithResponse request returning *DeleteV0CityByCityNameAgentByBaseResponse -func (c *ClientWithResponses) DeleteV0CityByCityNameAgentByBaseWithResponse(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameAgentByBaseResponse, error) { - rsp, err := c.DeleteV0CityByCityNameAgentByBase(ctx, cityName, base, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNameAgentByBaseWithResponse(ctx context.Context, cityName string, base string, params *DeleteV0CityByCityNameAgentByBaseParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameAgentByBaseResponse, error) { + rsp, err := c.DeleteV0CityByCityNameAgentByBase(ctx, cityName, base, params, reqEditors...) if err != nil { return nil, err } @@ -18281,16 +23555,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameAgentByBaseWithResponse(ctx con } // PatchV0CityByCityNameAgentByBaseWithBodyWithResponse request with arbitrary body returning *PatchV0CityByCityNameAgentByBaseResponse -func (c *ClientWithResponses) PatchV0CityByCityNameAgentByBaseWithBodyWithResponse(ctx context.Context, cityName string, base string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByBaseResponse, error) { - rsp, err := c.PatchV0CityByCityNameAgentByBaseWithBody(ctx, cityName, base, contentType, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameAgentByBaseWithBodyWithResponse(ctx context.Context, cityName string, base string, params *PatchV0CityByCityNameAgentByBaseParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByBaseResponse, error) { + rsp, err := c.PatchV0CityByCityNameAgentByBaseWithBody(ctx, cityName, base, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePatchV0CityByCityNameAgentByBaseResponse(rsp) } -func (c *ClientWithResponses) PatchV0CityByCityNameAgentByBaseWithResponse(ctx context.Context, cityName string, base string, body PatchV0CityByCityNameAgentByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByBaseResponse, error) { - rsp, err := c.PatchV0CityByCityNameAgentByBase(ctx, cityName, base, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameAgentByBaseWithResponse(ctx context.Context, cityName string, base string, params *PatchV0CityByCityNameAgentByBaseParams, body PatchV0CityByCityNameAgentByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByBaseResponse, error) { + rsp, err := c.PatchV0CityByCityNameAgentByBase(ctx, cityName, base, params, body, reqEditors...) if err != nil { return nil, err } @@ -18316,8 +23590,8 @@ func (c *ClientWithResponses) StreamAgentOutputWithResponse(ctx context.Context, } // PostV0CityByCityNameAgentByBaseByActionWithResponse request returning *PostV0CityByCityNameAgentByBaseByActionResponse -func (c *ClientWithResponses) PostV0CityByCityNameAgentByBaseByActionWithResponse(ctx context.Context, cityName string, base string, action PostV0CityByCityNameAgentByBaseByActionParamsAction, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameAgentByBaseByActionResponse, error) { - rsp, err := c.PostV0CityByCityNameAgentByBaseByAction(ctx, cityName, base, action, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameAgentByBaseByActionWithResponse(ctx context.Context, cityName string, base string, action PostV0CityByCityNameAgentByBaseByActionParamsAction, params *PostV0CityByCityNameAgentByBaseByActionParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameAgentByBaseByActionResponse, error) { + rsp, err := c.PostV0CityByCityNameAgentByBaseByAction(ctx, cityName, base, action, params, reqEditors...) if err != nil { return nil, err } @@ -18325,8 +23599,8 @@ func (c *ClientWithResponses) PostV0CityByCityNameAgentByBaseByActionWithRespons } // DeleteV0CityByCityNameAgentByDirByBaseWithResponse request returning *DeleteV0CityByCityNameAgentByDirByBaseResponse -func (c *ClientWithResponses) DeleteV0CityByCityNameAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameAgentByDirByBaseResponse, error) { - rsp, err := c.DeleteV0CityByCityNameAgentByDirByBase(ctx, cityName, dir, base, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNameAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, params *DeleteV0CityByCityNameAgentByDirByBaseParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameAgentByDirByBaseResponse, error) { + rsp, err := c.DeleteV0CityByCityNameAgentByDirByBase(ctx, cityName, dir, base, params, reqEditors...) if err != nil { return nil, err } @@ -18343,16 +23617,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameAgentByDirByBaseWithResponse(ct } // PatchV0CityByCityNameAgentByDirByBaseWithBodyWithResponse request with arbitrary body returning *PatchV0CityByCityNameAgentByDirByBaseResponse -func (c *ClientWithResponses) PatchV0CityByCityNameAgentByDirByBaseWithBodyWithResponse(ctx context.Context, cityName string, dir string, base string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByDirByBaseResponse, error) { - rsp, err := c.PatchV0CityByCityNameAgentByDirByBaseWithBody(ctx, cityName, dir, base, contentType, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameAgentByDirByBaseWithBodyWithResponse(ctx context.Context, cityName string, dir string, base string, params *PatchV0CityByCityNameAgentByDirByBaseParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByDirByBaseResponse, error) { + rsp, err := c.PatchV0CityByCityNameAgentByDirByBaseWithBody(ctx, cityName, dir, base, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePatchV0CityByCityNameAgentByDirByBaseResponse(rsp) } -func (c *ClientWithResponses) PatchV0CityByCityNameAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, body PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByDirByBaseResponse, error) { - rsp, err := c.PatchV0CityByCityNameAgentByDirByBase(ctx, cityName, dir, base, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, params *PatchV0CityByCityNameAgentByDirByBaseParams, body PatchV0CityByCityNameAgentByDirByBaseJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameAgentByDirByBaseResponse, error) { + rsp, err := c.PatchV0CityByCityNameAgentByDirByBase(ctx, cityName, dir, base, params, body, reqEditors...) if err != nil { return nil, err } @@ -18378,8 +23652,8 @@ func (c *ClientWithResponses) StreamAgentOutputQualifiedWithResponse(ctx context } // PostV0CityByCityNameAgentByDirByBaseByActionWithResponse request returning *PostV0CityByCityNameAgentByDirByBaseByActionResponse -func (c *ClientWithResponses) PostV0CityByCityNameAgentByDirByBaseByActionWithResponse(ctx context.Context, cityName string, dir string, base string, action PostV0CityByCityNameAgentByDirByBaseByActionParamsAction, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameAgentByDirByBaseByActionResponse, error) { - rsp, err := c.PostV0CityByCityNameAgentByDirByBaseByAction(ctx, cityName, dir, base, action, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameAgentByDirByBaseByActionWithResponse(ctx context.Context, cityName string, dir string, base string, action PostV0CityByCityNameAgentByDirByBaseByActionParamsAction, params *PostV0CityByCityNameAgentByDirByBaseByActionParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameAgentByDirByBaseByActionResponse, error) { + rsp, err := c.PostV0CityByCityNameAgentByDirByBaseByAction(ctx, cityName, dir, base, action, params, reqEditors...) if err != nil { return nil, err } @@ -18396,16 +23670,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameAgentsWithResponse(ctx context. } // CreateAgentWithBodyWithResponse request with arbitrary body returning *CreateAgentResponse -func (c *ClientWithResponses) CreateAgentWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateAgentResponse, error) { - rsp, err := c.CreateAgentWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) CreateAgentWithBodyWithResponse(ctx context.Context, cityName string, params *CreateAgentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateAgentResponse, error) { + rsp, err := c.CreateAgentWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseCreateAgentResponse(rsp) } -func (c *ClientWithResponses) CreateAgentWithResponse(ctx context.Context, cityName string, body CreateAgentJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateAgentResponse, error) { - rsp, err := c.CreateAgent(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) CreateAgentWithResponse(ctx context.Context, cityName string, params *CreateAgentParams, body CreateAgentJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateAgentResponse, error) { + rsp, err := c.CreateAgent(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18413,8 +23687,8 @@ func (c *ClientWithResponses) CreateAgentWithResponse(ctx context.Context, cityN } // DeleteV0CityByCityNameBeadByIdWithResponse request returning *DeleteV0CityByCityNameBeadByIdResponse -func (c *ClientWithResponses) DeleteV0CityByCityNameBeadByIdWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameBeadByIdResponse, error) { - rsp, err := c.DeleteV0CityByCityNameBeadById(ctx, cityName, id, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNameBeadByIdWithResponse(ctx context.Context, cityName string, id string, params *DeleteV0CityByCityNameBeadByIdParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameBeadByIdResponse, error) { + rsp, err := c.DeleteV0CityByCityNameBeadById(ctx, cityName, id, params, reqEditors...) if err != nil { return nil, err } @@ -18431,16 +23705,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameBeadByIdWithResponse(ctx contex } // PatchV0CityByCityNameBeadByIdWithBodyWithResponse request with arbitrary body returning *PatchV0CityByCityNameBeadByIdResponse -func (c *ClientWithResponses) PatchV0CityByCityNameBeadByIdWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameBeadByIdResponse, error) { - rsp, err := c.PatchV0CityByCityNameBeadByIdWithBody(ctx, cityName, id, contentType, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameBeadByIdWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameBeadByIdParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameBeadByIdResponse, error) { + rsp, err := c.PatchV0CityByCityNameBeadByIdWithBody(ctx, cityName, id, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePatchV0CityByCityNameBeadByIdResponse(rsp) } -func (c *ClientWithResponses) PatchV0CityByCityNameBeadByIdWithResponse(ctx context.Context, cityName string, id string, body PatchV0CityByCityNameBeadByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameBeadByIdResponse, error) { - rsp, err := c.PatchV0CityByCityNameBeadById(ctx, cityName, id, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameBeadByIdWithResponse(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameBeadByIdParams, body PatchV0CityByCityNameBeadByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameBeadByIdResponse, error) { + rsp, err := c.PatchV0CityByCityNameBeadById(ctx, cityName, id, params, body, reqEditors...) if err != nil { return nil, err } @@ -18448,16 +23722,16 @@ func (c *ClientWithResponses) PatchV0CityByCityNameBeadByIdWithResponse(ctx cont } // PostV0CityByCityNameBeadByIdAssignWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameBeadByIdAssignResponse -func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdAssignWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdAssignResponse, error) { - rsp, err := c.PostV0CityByCityNameBeadByIdAssignWithBody(ctx, cityName, id, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdAssignWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdAssignParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdAssignResponse, error) { + rsp, err := c.PostV0CityByCityNameBeadByIdAssignWithBody(ctx, cityName, id, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameBeadByIdAssignResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdAssignWithResponse(ctx context.Context, cityName string, id string, body PostV0CityByCityNameBeadByIdAssignJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdAssignResponse, error) { - rsp, err := c.PostV0CityByCityNameBeadByIdAssign(ctx, cityName, id, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdAssignWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdAssignParams, body PostV0CityByCityNameBeadByIdAssignJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdAssignResponse, error) { + rsp, err := c.PostV0CityByCityNameBeadByIdAssign(ctx, cityName, id, params, body, reqEditors...) if err != nil { return nil, err } @@ -18465,8 +23739,8 @@ func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdAssignWithResponse(ctx } // PostV0CityByCityNameBeadByIdCloseWithResponse request returning *PostV0CityByCityNameBeadByIdCloseResponse -func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdCloseWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdCloseResponse, error) { - rsp, err := c.PostV0CityByCityNameBeadByIdClose(ctx, cityName, id, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdCloseWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdCloseParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdCloseResponse, error) { + rsp, err := c.PostV0CityByCityNameBeadByIdClose(ctx, cityName, id, params, reqEditors...) if err != nil { return nil, err } @@ -18483,8 +23757,8 @@ func (c *ClientWithResponses) GetV0CityByCityNameBeadByIdDepsWithResponse(ctx co } // PostV0CityByCityNameBeadByIdReopenWithResponse request returning *PostV0CityByCityNameBeadByIdReopenResponse -func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdReopenWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdReopenResponse, error) { - rsp, err := c.PostV0CityByCityNameBeadByIdReopen(ctx, cityName, id, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdReopenWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdReopenParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdReopenResponse, error) { + rsp, err := c.PostV0CityByCityNameBeadByIdReopen(ctx, cityName, id, params, reqEditors...) if err != nil { return nil, err } @@ -18492,16 +23766,16 @@ func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdReopenWithResponse(ctx } // PostV0CityByCityNameBeadByIdUpdateWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameBeadByIdUpdateResponse -func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdUpdateWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdUpdateResponse, error) { - rsp, err := c.PostV0CityByCityNameBeadByIdUpdateWithBody(ctx, cityName, id, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdUpdateWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdUpdateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdUpdateResponse, error) { + rsp, err := c.PostV0CityByCityNameBeadByIdUpdateWithBody(ctx, cityName, id, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameBeadByIdUpdateResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdUpdateWithResponse(ctx context.Context, cityName string, id string, body PostV0CityByCityNameBeadByIdUpdateJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdUpdateResponse, error) { - rsp, err := c.PostV0CityByCityNameBeadByIdUpdate(ctx, cityName, id, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameBeadByIdUpdateWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameBeadByIdUpdateParams, body PostV0CityByCityNameBeadByIdUpdateJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameBeadByIdUpdateResponse, error) { + rsp, err := c.PostV0CityByCityNameBeadByIdUpdate(ctx, cityName, id, params, body, reqEditors...) if err != nil { return nil, err } @@ -18580,8 +23854,8 @@ func (c *ClientWithResponses) GetV0CityByCityNameConfigValidateWithResponse(ctx } // DeleteV0CityByCityNameConvoyByIdWithResponse request returning *DeleteV0CityByCityNameConvoyByIdResponse -func (c *ClientWithResponses) DeleteV0CityByCityNameConvoyByIdWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameConvoyByIdResponse, error) { - rsp, err := c.DeleteV0CityByCityNameConvoyById(ctx, cityName, id, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNameConvoyByIdWithResponse(ctx context.Context, cityName string, id string, params *DeleteV0CityByCityNameConvoyByIdParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameConvoyByIdResponse, error) { + rsp, err := c.DeleteV0CityByCityNameConvoyById(ctx, cityName, id, params, reqEditors...) if err != nil { return nil, err } @@ -18598,16 +23872,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameConvoyByIdWithResponse(ctx cont } // PostV0CityByCityNameConvoyByIdAddWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameConvoyByIdAddResponse -func (c *ClientWithResponses) PostV0CityByCityNameConvoyByIdAddWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdAddResponse, error) { - rsp, err := c.PostV0CityByCityNameConvoyByIdAddWithBody(ctx, cityName, id, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameConvoyByIdAddWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdAddParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdAddResponse, error) { + rsp, err := c.PostV0CityByCityNameConvoyByIdAddWithBody(ctx, cityName, id, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameConvoyByIdAddResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameConvoyByIdAddWithResponse(ctx context.Context, cityName string, id string, body PostV0CityByCityNameConvoyByIdAddJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdAddResponse, error) { - rsp, err := c.PostV0CityByCityNameConvoyByIdAdd(ctx, cityName, id, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameConvoyByIdAddWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdAddParams, body PostV0CityByCityNameConvoyByIdAddJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdAddResponse, error) { + rsp, err := c.PostV0CityByCityNameConvoyByIdAdd(ctx, cityName, id, params, body, reqEditors...) if err != nil { return nil, err } @@ -18624,8 +23898,8 @@ func (c *ClientWithResponses) GetV0CityByCityNameConvoyByIdCheckWithResponse(ctx } // PostV0CityByCityNameConvoyByIdCloseWithResponse request returning *PostV0CityByCityNameConvoyByIdCloseResponse -func (c *ClientWithResponses) PostV0CityByCityNameConvoyByIdCloseWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdCloseResponse, error) { - rsp, err := c.PostV0CityByCityNameConvoyByIdClose(ctx, cityName, id, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameConvoyByIdCloseWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdCloseParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdCloseResponse, error) { + rsp, err := c.PostV0CityByCityNameConvoyByIdClose(ctx, cityName, id, params, reqEditors...) if err != nil { return nil, err } @@ -18633,16 +23907,16 @@ func (c *ClientWithResponses) PostV0CityByCityNameConvoyByIdCloseWithResponse(ct } // PostV0CityByCityNameConvoyByIdRemoveWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameConvoyByIdRemoveResponse -func (c *ClientWithResponses) PostV0CityByCityNameConvoyByIdRemoveWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdRemoveResponse, error) { - rsp, err := c.PostV0CityByCityNameConvoyByIdRemoveWithBody(ctx, cityName, id, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameConvoyByIdRemoveWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdRemoveParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdRemoveResponse, error) { + rsp, err := c.PostV0CityByCityNameConvoyByIdRemoveWithBody(ctx, cityName, id, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameConvoyByIdRemoveResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameConvoyByIdRemoveWithResponse(ctx context.Context, cityName string, id string, body PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdRemoveResponse, error) { - rsp, err := c.PostV0CityByCityNameConvoyByIdRemove(ctx, cityName, id, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameConvoyByIdRemoveWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameConvoyByIdRemoveParams, body PostV0CityByCityNameConvoyByIdRemoveJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameConvoyByIdRemoveResponse, error) { + rsp, err := c.PostV0CityByCityNameConvoyByIdRemove(ctx, cityName, id, params, body, reqEditors...) if err != nil { return nil, err } @@ -18659,16 +23933,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameConvoysWithResponse(ctx context } // CreateConvoyWithBodyWithResponse request with arbitrary body returning *CreateConvoyResponse -func (c *ClientWithResponses) CreateConvoyWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateConvoyResponse, error) { - rsp, err := c.CreateConvoyWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) CreateConvoyWithBodyWithResponse(ctx context.Context, cityName string, params *CreateConvoyParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateConvoyResponse, error) { + rsp, err := c.CreateConvoyWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseCreateConvoyResponse(rsp) } -func (c *ClientWithResponses) CreateConvoyWithResponse(ctx context.Context, cityName string, body CreateConvoyJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateConvoyResponse, error) { - rsp, err := c.CreateConvoy(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) CreateConvoyWithResponse(ctx context.Context, cityName string, params *CreateConvoyParams, body CreateConvoyJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateConvoyResponse, error) { + rsp, err := c.CreateConvoy(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18685,16 +23959,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameEventsWithResponse(ctx context. } // EmitEventWithBodyWithResponse request with arbitrary body returning *EmitEventResponse -func (c *ClientWithResponses) EmitEventWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*EmitEventResponse, error) { - rsp, err := c.EmitEventWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) EmitEventWithBodyWithResponse(ctx context.Context, cityName string, params *EmitEventParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*EmitEventResponse, error) { + rsp, err := c.EmitEventWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseEmitEventResponse(rsp) } -func (c *ClientWithResponses) EmitEventWithResponse(ctx context.Context, cityName string, body EmitEventJSONRequestBody, reqEditors ...RequestEditorFn) (*EmitEventResponse, error) { - rsp, err := c.EmitEvent(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) EmitEventWithResponse(ctx context.Context, cityName string, params *EmitEventParams, body EmitEventJSONRequestBody, reqEditors ...RequestEditorFn) (*EmitEventResponse, error) { + rsp, err := c.EmitEvent(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18711,16 +23985,16 @@ func (c *ClientWithResponses) StreamEventsWithResponse(ctx context.Context, city } // DeleteV0CityByCityNameExtmsgAdaptersWithBodyWithResponse request with arbitrary body returning *DeleteV0CityByCityNameExtmsgAdaptersResponse -func (c *ClientWithResponses) DeleteV0CityByCityNameExtmsgAdaptersWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgAdaptersResponse, error) { - rsp, err := c.DeleteV0CityByCityNameExtmsgAdaptersWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNameExtmsgAdaptersWithBodyWithResponse(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgAdaptersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgAdaptersResponse, error) { + rsp, err := c.DeleteV0CityByCityNameExtmsgAdaptersWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseDeleteV0CityByCityNameExtmsgAdaptersResponse(rsp) } -func (c *ClientWithResponses) DeleteV0CityByCityNameExtmsgAdaptersWithResponse(ctx context.Context, cityName string, body DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgAdaptersResponse, error) { - rsp, err := c.DeleteV0CityByCityNameExtmsgAdapters(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNameExtmsgAdaptersWithResponse(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgAdaptersParams, body DeleteV0CityByCityNameExtmsgAdaptersJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgAdaptersResponse, error) { + rsp, err := c.DeleteV0CityByCityNameExtmsgAdapters(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18737,16 +24011,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameExtmsgAdaptersWithResponse(ctx } // RegisterExtmsgAdapterWithBodyWithResponse request with arbitrary body returning *RegisterExtmsgAdapterResponse -func (c *ClientWithResponses) RegisterExtmsgAdapterWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RegisterExtmsgAdapterResponse, error) { - rsp, err := c.RegisterExtmsgAdapterWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) RegisterExtmsgAdapterWithBodyWithResponse(ctx context.Context, cityName string, params *RegisterExtmsgAdapterParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RegisterExtmsgAdapterResponse, error) { + rsp, err := c.RegisterExtmsgAdapterWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseRegisterExtmsgAdapterResponse(rsp) } -func (c *ClientWithResponses) RegisterExtmsgAdapterWithResponse(ctx context.Context, cityName string, body RegisterExtmsgAdapterJSONRequestBody, reqEditors ...RequestEditorFn) (*RegisterExtmsgAdapterResponse, error) { - rsp, err := c.RegisterExtmsgAdapter(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) RegisterExtmsgAdapterWithResponse(ctx context.Context, cityName string, params *RegisterExtmsgAdapterParams, body RegisterExtmsgAdapterJSONRequestBody, reqEditors ...RequestEditorFn) (*RegisterExtmsgAdapterResponse, error) { + rsp, err := c.RegisterExtmsgAdapter(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18754,16 +24028,16 @@ func (c *ClientWithResponses) RegisterExtmsgAdapterWithResponse(ctx context.Cont } // PostV0CityByCityNameExtmsgBindWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameExtmsgBindResponse -func (c *ClientWithResponses) PostV0CityByCityNameExtmsgBindWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgBindResponse, error) { - rsp, err := c.PostV0CityByCityNameExtmsgBindWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameExtmsgBindWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgBindParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgBindResponse, error) { + rsp, err := c.PostV0CityByCityNameExtmsgBindWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameExtmsgBindResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameExtmsgBindWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgBindJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgBindResponse, error) { - rsp, err := c.PostV0CityByCityNameExtmsgBind(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameExtmsgBindWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgBindParams, body PostV0CityByCityNameExtmsgBindJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgBindResponse, error) { + rsp, err := c.PostV0CityByCityNameExtmsgBind(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18789,16 +24063,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameExtmsgGroupsWithResponse(ctx co } // EnsureExtmsgGroupWithBodyWithResponse request with arbitrary body returning *EnsureExtmsgGroupResponse -func (c *ClientWithResponses) EnsureExtmsgGroupWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*EnsureExtmsgGroupResponse, error) { - rsp, err := c.EnsureExtmsgGroupWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) EnsureExtmsgGroupWithBodyWithResponse(ctx context.Context, cityName string, params *EnsureExtmsgGroupParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*EnsureExtmsgGroupResponse, error) { + rsp, err := c.EnsureExtmsgGroupWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseEnsureExtmsgGroupResponse(rsp) } -func (c *ClientWithResponses) EnsureExtmsgGroupWithResponse(ctx context.Context, cityName string, body EnsureExtmsgGroupJSONRequestBody, reqEditors ...RequestEditorFn) (*EnsureExtmsgGroupResponse, error) { - rsp, err := c.EnsureExtmsgGroup(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) EnsureExtmsgGroupWithResponse(ctx context.Context, cityName string, params *EnsureExtmsgGroupParams, body EnsureExtmsgGroupJSONRequestBody, reqEditors ...RequestEditorFn) (*EnsureExtmsgGroupResponse, error) { + rsp, err := c.EnsureExtmsgGroup(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18806,16 +24080,16 @@ func (c *ClientWithResponses) EnsureExtmsgGroupWithResponse(ctx context.Context, } // PostV0CityByCityNameExtmsgInboundWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameExtmsgInboundResponse -func (c *ClientWithResponses) PostV0CityByCityNameExtmsgInboundWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgInboundResponse, error) { - rsp, err := c.PostV0CityByCityNameExtmsgInboundWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameExtmsgInboundWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgInboundParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgInboundResponse, error) { + rsp, err := c.PostV0CityByCityNameExtmsgInboundWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameExtmsgInboundResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameExtmsgInboundWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgInboundJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgInboundResponse, error) { - rsp, err := c.PostV0CityByCityNameExtmsgInbound(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameExtmsgInboundWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgInboundParams, body PostV0CityByCityNameExtmsgInboundJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgInboundResponse, error) { + rsp, err := c.PostV0CityByCityNameExtmsgInbound(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18823,16 +24097,16 @@ func (c *ClientWithResponses) PostV0CityByCityNameExtmsgInboundWithResponse(ctx } // PostV0CityByCityNameExtmsgOutboundWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameExtmsgOutboundResponse -func (c *ClientWithResponses) PostV0CityByCityNameExtmsgOutboundWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgOutboundResponse, error) { - rsp, err := c.PostV0CityByCityNameExtmsgOutboundWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameExtmsgOutboundWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgOutboundParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgOutboundResponse, error) { + rsp, err := c.PostV0CityByCityNameExtmsgOutboundWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameExtmsgOutboundResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameExtmsgOutboundWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgOutboundJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgOutboundResponse, error) { - rsp, err := c.PostV0CityByCityNameExtmsgOutbound(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameExtmsgOutboundWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgOutboundParams, body PostV0CityByCityNameExtmsgOutboundJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgOutboundResponse, error) { + rsp, err := c.PostV0CityByCityNameExtmsgOutbound(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18840,16 +24114,16 @@ func (c *ClientWithResponses) PostV0CityByCityNameExtmsgOutboundWithResponse(ctx } // DeleteV0CityByCityNameExtmsgParticipantsWithBodyWithResponse request with arbitrary body returning *DeleteV0CityByCityNameExtmsgParticipantsResponse -func (c *ClientWithResponses) DeleteV0CityByCityNameExtmsgParticipantsWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgParticipantsResponse, error) { - rsp, err := c.DeleteV0CityByCityNameExtmsgParticipantsWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNameExtmsgParticipantsWithBodyWithResponse(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgParticipantsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgParticipantsResponse, error) { + rsp, err := c.DeleteV0CityByCityNameExtmsgParticipantsWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseDeleteV0CityByCityNameExtmsgParticipantsResponse(rsp) } -func (c *ClientWithResponses) DeleteV0CityByCityNameExtmsgParticipantsWithResponse(ctx context.Context, cityName string, body DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgParticipantsResponse, error) { - rsp, err := c.DeleteV0CityByCityNameExtmsgParticipants(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNameExtmsgParticipantsWithResponse(ctx context.Context, cityName string, params *DeleteV0CityByCityNameExtmsgParticipantsParams, body DeleteV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameExtmsgParticipantsResponse, error) { + rsp, err := c.DeleteV0CityByCityNameExtmsgParticipants(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18857,16 +24131,16 @@ func (c *ClientWithResponses) DeleteV0CityByCityNameExtmsgParticipantsWithRespon } // PostV0CityByCityNameExtmsgParticipantsWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameExtmsgParticipantsResponse -func (c *ClientWithResponses) PostV0CityByCityNameExtmsgParticipantsWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgParticipantsResponse, error) { - rsp, err := c.PostV0CityByCityNameExtmsgParticipantsWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameExtmsgParticipantsWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgParticipantsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgParticipantsResponse, error) { + rsp, err := c.PostV0CityByCityNameExtmsgParticipantsWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameExtmsgParticipantsResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameExtmsgParticipantsWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgParticipantsResponse, error) { - rsp, err := c.PostV0CityByCityNameExtmsgParticipants(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameExtmsgParticipantsWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgParticipantsParams, body PostV0CityByCityNameExtmsgParticipantsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgParticipantsResponse, error) { + rsp, err := c.PostV0CityByCityNameExtmsgParticipants(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18883,16 +24157,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameExtmsgTranscriptWithResponse(ct } // PostV0CityByCityNameExtmsgTranscriptAckWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameExtmsgTranscriptAckResponse -func (c *ClientWithResponses) PostV0CityByCityNameExtmsgTranscriptAckWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgTranscriptAckResponse, error) { - rsp, err := c.PostV0CityByCityNameExtmsgTranscriptAckWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameExtmsgTranscriptAckWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgTranscriptAckParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgTranscriptAckResponse, error) { + rsp, err := c.PostV0CityByCityNameExtmsgTranscriptAckWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameExtmsgTranscriptAckResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameExtmsgTranscriptAckWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgTranscriptAckResponse, error) { - rsp, err := c.PostV0CityByCityNameExtmsgTranscriptAck(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameExtmsgTranscriptAckWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgTranscriptAckParams, body PostV0CityByCityNameExtmsgTranscriptAckJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgTranscriptAckResponse, error) { + rsp, err := c.PostV0CityByCityNameExtmsgTranscriptAck(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18900,16 +24174,16 @@ func (c *ClientWithResponses) PostV0CityByCityNameExtmsgTranscriptAckWithRespons } // PostV0CityByCityNameExtmsgUnbindWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameExtmsgUnbindResponse -func (c *ClientWithResponses) PostV0CityByCityNameExtmsgUnbindWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgUnbindResponse, error) { - rsp, err := c.PostV0CityByCityNameExtmsgUnbindWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameExtmsgUnbindWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgUnbindParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgUnbindResponse, error) { + rsp, err := c.PostV0CityByCityNameExtmsgUnbindWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameExtmsgUnbindResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameExtmsgUnbindWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameExtmsgUnbindJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgUnbindResponse, error) { - rsp, err := c.PostV0CityByCityNameExtmsgUnbind(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameExtmsgUnbindWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameExtmsgUnbindParams, body PostV0CityByCityNameExtmsgUnbindJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameExtmsgUnbindResponse, error) { + rsp, err := c.PostV0CityByCityNameExtmsgUnbind(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -18953,16 +24227,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameFormulasByNameWithResponse(ctx } // PostV0CityByCityNameFormulasByNamePreviewWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameFormulasByNamePreviewResponse -func (c *ClientWithResponses) PostV0CityByCityNameFormulasByNamePreviewWithBodyWithResponse(ctx context.Context, cityName string, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameFormulasByNamePreviewResponse, error) { - rsp, err := c.PostV0CityByCityNameFormulasByNamePreviewWithBody(ctx, cityName, name, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameFormulasByNamePreviewWithBodyWithResponse(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameFormulasByNamePreviewParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameFormulasByNamePreviewResponse, error) { + rsp, err := c.PostV0CityByCityNameFormulasByNamePreviewWithBody(ctx, cityName, name, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameFormulasByNamePreviewResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameFormulasByNamePreviewWithResponse(ctx context.Context, cityName string, name string, body PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameFormulasByNamePreviewResponse, error) { - rsp, err := c.PostV0CityByCityNameFormulasByNamePreview(ctx, cityName, name, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameFormulasByNamePreviewWithResponse(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameFormulasByNamePreviewParams, body PostV0CityByCityNameFormulasByNamePreviewJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameFormulasByNamePreviewResponse, error) { + rsp, err := c.PostV0CityByCityNameFormulasByNamePreview(ctx, cityName, name, params, body, reqEditors...) if err != nil { return nil, err } @@ -19112,8 +24386,8 @@ func (c *ClientWithResponses) GetV0CityByCityNameOrderByNameWithResponse(ctx con } // PostV0CityByCityNameOrderByNameDisableWithResponse request returning *PostV0CityByCityNameOrderByNameDisableResponse -func (c *ClientWithResponses) PostV0CityByCityNameOrderByNameDisableWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameOrderByNameDisableResponse, error) { - rsp, err := c.PostV0CityByCityNameOrderByNameDisable(ctx, cityName, name, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameOrderByNameDisableWithResponse(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameOrderByNameDisableParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameOrderByNameDisableResponse, error) { + rsp, err := c.PostV0CityByCityNameOrderByNameDisable(ctx, cityName, name, params, reqEditors...) if err != nil { return nil, err } @@ -19121,8 +24395,8 @@ func (c *ClientWithResponses) PostV0CityByCityNameOrderByNameDisableWithResponse } // PostV0CityByCityNameOrderByNameEnableWithResponse request returning *PostV0CityByCityNameOrderByNameEnableResponse -func (c *ClientWithResponses) PostV0CityByCityNameOrderByNameEnableWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameOrderByNameEnableResponse, error) { - rsp, err := c.PostV0CityByCityNameOrderByNameEnable(ctx, cityName, name, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameOrderByNameEnableWithResponse(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameOrderByNameEnableParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameOrderByNameEnableResponse, error) { + rsp, err := c.PostV0CityByCityNameOrderByNameEnable(ctx, cityName, name, params, reqEditors...) if err != nil { return nil, err } @@ -19175,8 +24449,8 @@ func (c *ClientWithResponses) GetV0CityByCityNamePacksWithResponse(ctx context.C } // DeleteV0CityByCityNamePatchesAgentByBaseWithResponse request returning *DeleteV0CityByCityNamePatchesAgentByBaseResponse -func (c *ClientWithResponses) DeleteV0CityByCityNamePatchesAgentByBaseWithResponse(ctx context.Context, cityName string, base string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesAgentByBaseResponse, error) { - rsp, err := c.DeleteV0CityByCityNamePatchesAgentByBase(ctx, cityName, base, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNamePatchesAgentByBaseWithResponse(ctx context.Context, cityName string, base string, params *DeleteV0CityByCityNamePatchesAgentByBaseParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesAgentByBaseResponse, error) { + rsp, err := c.DeleteV0CityByCityNamePatchesAgentByBase(ctx, cityName, base, params, reqEditors...) if err != nil { return nil, err } @@ -19193,8 +24467,8 @@ func (c *ClientWithResponses) GetV0CityByCityNamePatchesAgentByBaseWithResponse( } // DeleteV0CityByCityNamePatchesAgentByDirByBaseWithResponse request returning *DeleteV0CityByCityNamePatchesAgentByDirByBaseResponse -func (c *ClientWithResponses) DeleteV0CityByCityNamePatchesAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesAgentByDirByBaseResponse, error) { - rsp, err := c.DeleteV0CityByCityNamePatchesAgentByDirByBase(ctx, cityName, dir, base, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNamePatchesAgentByDirByBaseWithResponse(ctx context.Context, cityName string, dir string, base string, params *DeleteV0CityByCityNamePatchesAgentByDirByBaseParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesAgentByDirByBaseResponse, error) { + rsp, err := c.DeleteV0CityByCityNamePatchesAgentByDirByBase(ctx, cityName, dir, base, params, reqEditors...) if err != nil { return nil, err } @@ -19220,16 +24494,16 @@ func (c *ClientWithResponses) GetV0CityByCityNamePatchesAgentsWithResponse(ctx c } // PutV0CityByCityNamePatchesAgentsWithBodyWithResponse request with arbitrary body returning *PutV0CityByCityNamePatchesAgentsResponse -func (c *ClientWithResponses) PutV0CityByCityNamePatchesAgentsWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesAgentsResponse, error) { - rsp, err := c.PutV0CityByCityNamePatchesAgentsWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) PutV0CityByCityNamePatchesAgentsWithBodyWithResponse(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesAgentsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesAgentsResponse, error) { + rsp, err := c.PutV0CityByCityNamePatchesAgentsWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePutV0CityByCityNamePatchesAgentsResponse(rsp) } -func (c *ClientWithResponses) PutV0CityByCityNamePatchesAgentsWithResponse(ctx context.Context, cityName string, body PutV0CityByCityNamePatchesAgentsJSONRequestBody, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesAgentsResponse, error) { - rsp, err := c.PutV0CityByCityNamePatchesAgents(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) PutV0CityByCityNamePatchesAgentsWithResponse(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesAgentsParams, body PutV0CityByCityNamePatchesAgentsJSONRequestBody, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesAgentsResponse, error) { + rsp, err := c.PutV0CityByCityNamePatchesAgents(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -19237,8 +24511,8 @@ func (c *ClientWithResponses) PutV0CityByCityNamePatchesAgentsWithResponse(ctx c } // DeleteV0CityByCityNamePatchesProviderByNameWithResponse request returning *DeleteV0CityByCityNamePatchesProviderByNameResponse -func (c *ClientWithResponses) DeleteV0CityByCityNamePatchesProviderByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesProviderByNameResponse, error) { - rsp, err := c.DeleteV0CityByCityNamePatchesProviderByName(ctx, cityName, name, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNamePatchesProviderByNameWithResponse(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNamePatchesProviderByNameParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesProviderByNameResponse, error) { + rsp, err := c.DeleteV0CityByCityNamePatchesProviderByName(ctx, cityName, name, params, reqEditors...) if err != nil { return nil, err } @@ -19264,16 +24538,16 @@ func (c *ClientWithResponses) GetV0CityByCityNamePatchesProvidersWithResponse(ct } // PutV0CityByCityNamePatchesProvidersWithBodyWithResponse request with arbitrary body returning *PutV0CityByCityNamePatchesProvidersResponse -func (c *ClientWithResponses) PutV0CityByCityNamePatchesProvidersWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesProvidersResponse, error) { - rsp, err := c.PutV0CityByCityNamePatchesProvidersWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) PutV0CityByCityNamePatchesProvidersWithBodyWithResponse(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesProvidersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesProvidersResponse, error) { + rsp, err := c.PutV0CityByCityNamePatchesProvidersWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePutV0CityByCityNamePatchesProvidersResponse(rsp) } -func (c *ClientWithResponses) PutV0CityByCityNamePatchesProvidersWithResponse(ctx context.Context, cityName string, body PutV0CityByCityNamePatchesProvidersJSONRequestBody, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesProvidersResponse, error) { - rsp, err := c.PutV0CityByCityNamePatchesProviders(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) PutV0CityByCityNamePatchesProvidersWithResponse(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesProvidersParams, body PutV0CityByCityNamePatchesProvidersJSONRequestBody, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesProvidersResponse, error) { + rsp, err := c.PutV0CityByCityNamePatchesProviders(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -19281,8 +24555,8 @@ func (c *ClientWithResponses) PutV0CityByCityNamePatchesProvidersWithResponse(ct } // DeleteV0CityByCityNamePatchesRigByNameWithResponse request returning *DeleteV0CityByCityNamePatchesRigByNameResponse -func (c *ClientWithResponses) DeleteV0CityByCityNamePatchesRigByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesRigByNameResponse, error) { - rsp, err := c.DeleteV0CityByCityNamePatchesRigByName(ctx, cityName, name, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNamePatchesRigByNameWithResponse(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNamePatchesRigByNameParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNamePatchesRigByNameResponse, error) { + rsp, err := c.DeleteV0CityByCityNamePatchesRigByName(ctx, cityName, name, params, reqEditors...) if err != nil { return nil, err } @@ -19308,16 +24582,16 @@ func (c *ClientWithResponses) GetV0CityByCityNamePatchesRigsWithResponse(ctx con } // PutV0CityByCityNamePatchesRigsWithBodyWithResponse request with arbitrary body returning *PutV0CityByCityNamePatchesRigsResponse -func (c *ClientWithResponses) PutV0CityByCityNamePatchesRigsWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesRigsResponse, error) { - rsp, err := c.PutV0CityByCityNamePatchesRigsWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) PutV0CityByCityNamePatchesRigsWithBodyWithResponse(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesRigsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesRigsResponse, error) { + rsp, err := c.PutV0CityByCityNamePatchesRigsWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePutV0CityByCityNamePatchesRigsResponse(rsp) } -func (c *ClientWithResponses) PutV0CityByCityNamePatchesRigsWithResponse(ctx context.Context, cityName string, body PutV0CityByCityNamePatchesRigsJSONRequestBody, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesRigsResponse, error) { - rsp, err := c.PutV0CityByCityNamePatchesRigs(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) PutV0CityByCityNamePatchesRigsWithResponse(ctx context.Context, cityName string, params *PutV0CityByCityNamePatchesRigsParams, body PutV0CityByCityNamePatchesRigsJSONRequestBody, reqEditors ...RequestEditorFn) (*PutV0CityByCityNamePatchesRigsResponse, error) { + rsp, err := c.PutV0CityByCityNamePatchesRigs(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -19334,8 +24608,8 @@ func (c *ClientWithResponses) GetV0CityByCityNameProviderReadinessWithResponse(c } // DeleteV0CityByCityNameProviderByNameWithResponse request returning *DeleteV0CityByCityNameProviderByNameResponse -func (c *ClientWithResponses) DeleteV0CityByCityNameProviderByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameProviderByNameResponse, error) { - rsp, err := c.DeleteV0CityByCityNameProviderByName(ctx, cityName, name, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNameProviderByNameWithResponse(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNameProviderByNameParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameProviderByNameResponse, error) { + rsp, err := c.DeleteV0CityByCityNameProviderByName(ctx, cityName, name, params, reqEditors...) if err != nil { return nil, err } @@ -19352,16 +24626,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameProviderByNameWithResponse(ctx } // PatchV0CityByCityNameProviderByNameWithBodyWithResponse request with arbitrary body returning *PatchV0CityByCityNameProviderByNameResponse -func (c *ClientWithResponses) PatchV0CityByCityNameProviderByNameWithBodyWithResponse(ctx context.Context, cityName string, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameProviderByNameResponse, error) { - rsp, err := c.PatchV0CityByCityNameProviderByNameWithBody(ctx, cityName, name, contentType, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameProviderByNameWithBodyWithResponse(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameProviderByNameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameProviderByNameResponse, error) { + rsp, err := c.PatchV0CityByCityNameProviderByNameWithBody(ctx, cityName, name, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePatchV0CityByCityNameProviderByNameResponse(rsp) } -func (c *ClientWithResponses) PatchV0CityByCityNameProviderByNameWithResponse(ctx context.Context, cityName string, name string, body PatchV0CityByCityNameProviderByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameProviderByNameResponse, error) { - rsp, err := c.PatchV0CityByCityNameProviderByName(ctx, cityName, name, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameProviderByNameWithResponse(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameProviderByNameParams, body PatchV0CityByCityNameProviderByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameProviderByNameResponse, error) { + rsp, err := c.PatchV0CityByCityNameProviderByName(ctx, cityName, name, params, body, reqEditors...) if err != nil { return nil, err } @@ -19378,16 +24652,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameProvidersWithResponse(ctx conte } // CreateProviderWithBodyWithResponse request with arbitrary body returning *CreateProviderResponse -func (c *ClientWithResponses) CreateProviderWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateProviderResponse, error) { - rsp, err := c.CreateProviderWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) CreateProviderWithBodyWithResponse(ctx context.Context, cityName string, params *CreateProviderParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateProviderResponse, error) { + rsp, err := c.CreateProviderWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseCreateProviderResponse(rsp) } -func (c *ClientWithResponses) CreateProviderWithResponse(ctx context.Context, cityName string, body CreateProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateProviderResponse, error) { - rsp, err := c.CreateProvider(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) CreateProviderWithResponse(ctx context.Context, cityName string, params *CreateProviderParams, body CreateProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateProviderResponse, error) { + rsp, err := c.CreateProvider(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -19413,8 +24687,8 @@ func (c *ClientWithResponses) GetV0CityByCityNameReadinessWithResponse(ctx conte } // DeleteV0CityByCityNameRigByNameWithResponse request returning *DeleteV0CityByCityNameRigByNameResponse -func (c *ClientWithResponses) DeleteV0CityByCityNameRigByNameWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameRigByNameResponse, error) { - rsp, err := c.DeleteV0CityByCityNameRigByName(ctx, cityName, name, reqEditors...) +func (c *ClientWithResponses) DeleteV0CityByCityNameRigByNameWithResponse(ctx context.Context, cityName string, name string, params *DeleteV0CityByCityNameRigByNameParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameRigByNameResponse, error) { + rsp, err := c.DeleteV0CityByCityNameRigByName(ctx, cityName, name, params, reqEditors...) if err != nil { return nil, err } @@ -19431,16 +24705,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameRigByNameWithResponse(ctx conte } // PatchV0CityByCityNameRigByNameWithBodyWithResponse request with arbitrary body returning *PatchV0CityByCityNameRigByNameResponse -func (c *ClientWithResponses) PatchV0CityByCityNameRigByNameWithBodyWithResponse(ctx context.Context, cityName string, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameRigByNameResponse, error) { - rsp, err := c.PatchV0CityByCityNameRigByNameWithBody(ctx, cityName, name, contentType, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameRigByNameWithBodyWithResponse(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameRigByNameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameRigByNameResponse, error) { + rsp, err := c.PatchV0CityByCityNameRigByNameWithBody(ctx, cityName, name, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePatchV0CityByCityNameRigByNameResponse(rsp) } -func (c *ClientWithResponses) PatchV0CityByCityNameRigByNameWithResponse(ctx context.Context, cityName string, name string, body PatchV0CityByCityNameRigByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameRigByNameResponse, error) { - rsp, err := c.PatchV0CityByCityNameRigByName(ctx, cityName, name, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameRigByNameWithResponse(ctx context.Context, cityName string, name string, params *PatchV0CityByCityNameRigByNameParams, body PatchV0CityByCityNameRigByNameJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameRigByNameResponse, error) { + rsp, err := c.PatchV0CityByCityNameRigByName(ctx, cityName, name, params, body, reqEditors...) if err != nil { return nil, err } @@ -19448,8 +24722,8 @@ func (c *ClientWithResponses) PatchV0CityByCityNameRigByNameWithResponse(ctx con } // PostV0CityByCityNameRigByNameByActionWithResponse request returning *PostV0CityByCityNameRigByNameByActionResponse -func (c *ClientWithResponses) PostV0CityByCityNameRigByNameByActionWithResponse(ctx context.Context, cityName string, name string, action string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameRigByNameByActionResponse, error) { - rsp, err := c.PostV0CityByCityNameRigByNameByAction(ctx, cityName, name, action, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameRigByNameByActionWithResponse(ctx context.Context, cityName string, name string, action string, params *PostV0CityByCityNameRigByNameByActionParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameRigByNameByActionResponse, error) { + rsp, err := c.PostV0CityByCityNameRigByNameByAction(ctx, cityName, name, action, params, reqEditors...) if err != nil { return nil, err } @@ -19466,16 +24740,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameRigsWithResponse(ctx context.Co } // CreateRigWithBodyWithResponse request with arbitrary body returning *CreateRigResponse -func (c *ClientWithResponses) CreateRigWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateRigResponse, error) { - rsp, err := c.CreateRigWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) CreateRigWithBodyWithResponse(ctx context.Context, cityName string, params *CreateRigParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateRigResponse, error) { + rsp, err := c.CreateRigWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseCreateRigResponse(rsp) } -func (c *ClientWithResponses) CreateRigWithResponse(ctx context.Context, cityName string, body CreateRigJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateRigResponse, error) { - rsp, err := c.CreateRig(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) CreateRigWithResponse(ctx context.Context, cityName string, params *CreateRigParams, body CreateRigJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateRigResponse, error) { + rsp, err := c.CreateRig(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -19492,8 +24766,8 @@ func (c *ClientWithResponses) GetV0CityByCityNameServiceByNameWithResponse(ctx c } // PostV0CityByCityNameServiceByNameRestartWithResponse request returning *PostV0CityByCityNameServiceByNameRestartResponse -func (c *ClientWithResponses) PostV0CityByCityNameServiceByNameRestartWithResponse(ctx context.Context, cityName string, name string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameServiceByNameRestartResponse, error) { - rsp, err := c.PostV0CityByCityNameServiceByNameRestart(ctx, cityName, name, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameServiceByNameRestartWithResponse(ctx context.Context, cityName string, name string, params *PostV0CityByCityNameServiceByNameRestartParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameServiceByNameRestartResponse, error) { + rsp, err := c.PostV0CityByCityNameServiceByNameRestart(ctx, cityName, name, params, reqEditors...) if err != nil { return nil, err } @@ -19519,16 +24793,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameSessionByIdWithResponse(ctx con } // PatchV0CityByCityNameSessionByIdWithBodyWithResponse request with arbitrary body returning *PatchV0CityByCityNameSessionByIdResponse -func (c *ClientWithResponses) PatchV0CityByCityNameSessionByIdWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameSessionByIdResponse, error) { - rsp, err := c.PatchV0CityByCityNameSessionByIdWithBody(ctx, cityName, id, contentType, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameSessionByIdWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameSessionByIdParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameSessionByIdResponse, error) { + rsp, err := c.PatchV0CityByCityNameSessionByIdWithBody(ctx, cityName, id, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePatchV0CityByCityNameSessionByIdResponse(rsp) } -func (c *ClientWithResponses) PatchV0CityByCityNameSessionByIdWithResponse(ctx context.Context, cityName string, id string, body PatchV0CityByCityNameSessionByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameSessionByIdResponse, error) { - rsp, err := c.PatchV0CityByCityNameSessionById(ctx, cityName, id, body, reqEditors...) +func (c *ClientWithResponses) PatchV0CityByCityNameSessionByIdWithResponse(ctx context.Context, cityName string, id string, params *PatchV0CityByCityNameSessionByIdParams, body PatchV0CityByCityNameSessionByIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV0CityByCityNameSessionByIdResponse, error) { + rsp, err := c.PatchV0CityByCityNameSessionById(ctx, cityName, id, params, body, reqEditors...) if err != nil { return nil, err } @@ -19563,8 +24837,8 @@ func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdCloseWithResponse(c } // PostV0CityByCityNameSessionByIdKillWithResponse request returning *PostV0CityByCityNameSessionByIdKillResponse -func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdKillWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdKillResponse, error) { - rsp, err := c.PostV0CityByCityNameSessionByIdKill(ctx, cityName, id, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdKillWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdKillParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdKillResponse, error) { + rsp, err := c.PostV0CityByCityNameSessionByIdKill(ctx, cityName, id, params, reqEditors...) if err != nil { return nil, err } @@ -19572,16 +24846,16 @@ func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdKillWithResponse(ct } // SendSessionMessageWithBodyWithResponse request with arbitrary body returning *SendSessionMessageResponse -func (c *ClientWithResponses) SendSessionMessageWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SendSessionMessageResponse, error) { - rsp, err := c.SendSessionMessageWithBody(ctx, cityName, id, contentType, body, reqEditors...) +func (c *ClientWithResponses) SendSessionMessageWithBodyWithResponse(ctx context.Context, cityName string, id string, params *SendSessionMessageParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SendSessionMessageResponse, error) { + rsp, err := c.SendSessionMessageWithBody(ctx, cityName, id, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseSendSessionMessageResponse(rsp) } -func (c *ClientWithResponses) SendSessionMessageWithResponse(ctx context.Context, cityName string, id string, body SendSessionMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*SendSessionMessageResponse, error) { - rsp, err := c.SendSessionMessage(ctx, cityName, id, body, reqEditors...) +func (c *ClientWithResponses) SendSessionMessageWithResponse(ctx context.Context, cityName string, id string, params *SendSessionMessageParams, body SendSessionMessageJSONRequestBody, reqEditors ...RequestEditorFn) (*SendSessionMessageResponse, error) { + rsp, err := c.SendSessionMessage(ctx, cityName, id, params, body, reqEditors...) if err != nil { return nil, err } @@ -19598,16 +24872,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameSessionByIdPendingWithResponse( } // PostV0CityByCityNameSessionByIdRenameWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameSessionByIdRenameResponse -func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdRenameWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdRenameResponse, error) { - rsp, err := c.PostV0CityByCityNameSessionByIdRenameWithBody(ctx, cityName, id, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdRenameWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdRenameResponse, error) { + rsp, err := c.PostV0CityByCityNameSessionByIdRenameWithBody(ctx, cityName, id, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameSessionByIdRenameResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdRenameWithResponse(ctx context.Context, cityName string, id string, body PostV0CityByCityNameSessionByIdRenameJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdRenameResponse, error) { - rsp, err := c.PostV0CityByCityNameSessionByIdRename(ctx, cityName, id, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdRenameWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, body PostV0CityByCityNameSessionByIdRenameJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdRenameResponse, error) { + rsp, err := c.PostV0CityByCityNameSessionByIdRename(ctx, cityName, id, params, body, reqEditors...) if err != nil { return nil, err } @@ -19615,16 +24889,16 @@ func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdRenameWithResponse( } // RespondSessionWithBodyWithResponse request with arbitrary body returning *RespondSessionResponse -func (c *ClientWithResponses) RespondSessionWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RespondSessionResponse, error) { - rsp, err := c.RespondSessionWithBody(ctx, cityName, id, contentType, body, reqEditors...) +func (c *ClientWithResponses) RespondSessionWithBodyWithResponse(ctx context.Context, cityName string, id string, params *RespondSessionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RespondSessionResponse, error) { + rsp, err := c.RespondSessionWithBody(ctx, cityName, id, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseRespondSessionResponse(rsp) } -func (c *ClientWithResponses) RespondSessionWithResponse(ctx context.Context, cityName string, id string, body RespondSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*RespondSessionResponse, error) { - rsp, err := c.RespondSession(ctx, cityName, id, body, reqEditors...) +func (c *ClientWithResponses) RespondSessionWithResponse(ctx context.Context, cityName string, id string, params *RespondSessionParams, body RespondSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*RespondSessionResponse, error) { + rsp, err := c.RespondSession(ctx, cityName, id, params, body, reqEditors...) if err != nil { return nil, err } @@ -19632,8 +24906,8 @@ func (c *ClientWithResponses) RespondSessionWithResponse(ctx context.Context, ci } // PostV0CityByCityNameSessionByIdStopWithResponse request returning *PostV0CityByCityNameSessionByIdStopResponse -func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdStopWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdStopResponse, error) { - rsp, err := c.PostV0CityByCityNameSessionByIdStop(ctx, cityName, id, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdStopWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdStopParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdStopResponse, error) { + rsp, err := c.PostV0CityByCityNameSessionByIdStop(ctx, cityName, id, params, reqEditors...) if err != nil { return nil, err } @@ -19650,16 +24924,16 @@ func (c *ClientWithResponses) StreamSessionWithResponse(ctx context.Context, cit } // SubmitSessionWithBodyWithResponse request with arbitrary body returning *SubmitSessionResponse -func (c *ClientWithResponses) SubmitSessionWithBodyWithResponse(ctx context.Context, cityName string, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SubmitSessionResponse, error) { - rsp, err := c.SubmitSessionWithBody(ctx, cityName, id, contentType, body, reqEditors...) +func (c *ClientWithResponses) SubmitSessionWithBodyWithResponse(ctx context.Context, cityName string, id string, params *SubmitSessionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SubmitSessionResponse, error) { + rsp, err := c.SubmitSessionWithBody(ctx, cityName, id, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseSubmitSessionResponse(rsp) } -func (c *ClientWithResponses) SubmitSessionWithResponse(ctx context.Context, cityName string, id string, body SubmitSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*SubmitSessionResponse, error) { - rsp, err := c.SubmitSession(ctx, cityName, id, body, reqEditors...) +func (c *ClientWithResponses) SubmitSessionWithResponse(ctx context.Context, cityName string, id string, params *SubmitSessionParams, body SubmitSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*SubmitSessionResponse, error) { + rsp, err := c.SubmitSession(ctx, cityName, id, params, body, reqEditors...) if err != nil { return nil, err } @@ -19667,8 +24941,8 @@ func (c *ClientWithResponses) SubmitSessionWithResponse(ctx context.Context, cit } // PostV0CityByCityNameSessionByIdSuspendWithResponse request returning *PostV0CityByCityNameSessionByIdSuspendResponse -func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdSuspendWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdSuspendResponse, error) { - rsp, err := c.PostV0CityByCityNameSessionByIdSuspend(ctx, cityName, id, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdSuspendWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdSuspendParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdSuspendResponse, error) { + rsp, err := c.PostV0CityByCityNameSessionByIdSuspend(ctx, cityName, id, params, reqEditors...) if err != nil { return nil, err } @@ -19685,8 +24959,8 @@ func (c *ClientWithResponses) GetV0CityByCityNameSessionByIdTranscriptWithRespon } // PostV0CityByCityNameSessionByIdWakeWithResponse request returning *PostV0CityByCityNameSessionByIdWakeResponse -func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdWakeWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdWakeResponse, error) { - rsp, err := c.PostV0CityByCityNameSessionByIdWake(ctx, cityName, id, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdWakeWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdWakeParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdWakeResponse, error) { + rsp, err := c.PostV0CityByCityNameSessionByIdWake(ctx, cityName, id, params, reqEditors...) if err != nil { return nil, err } @@ -19703,16 +24977,16 @@ func (c *ClientWithResponses) GetV0CityByCityNameSessionsWithResponse(ctx contex } // CreateSessionWithBodyWithResponse request with arbitrary body returning *CreateSessionResponse -func (c *ClientWithResponses) CreateSessionWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateSessionResponse, error) { - rsp, err := c.CreateSessionWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) CreateSessionWithBodyWithResponse(ctx context.Context, cityName string, params *CreateSessionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateSessionResponse, error) { + rsp, err := c.CreateSessionWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParseCreateSessionResponse(rsp) } -func (c *ClientWithResponses) CreateSessionWithResponse(ctx context.Context, cityName string, body CreateSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateSessionResponse, error) { - rsp, err := c.CreateSession(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) CreateSessionWithResponse(ctx context.Context, cityName string, params *CreateSessionParams, body CreateSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateSessionResponse, error) { + rsp, err := c.CreateSession(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -19720,16 +24994,16 @@ func (c *ClientWithResponses) CreateSessionWithResponse(ctx context.Context, cit } // PostV0CityByCityNameSlingWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameSlingResponse -func (c *ClientWithResponses) PostV0CityByCityNameSlingWithBodyWithResponse(ctx context.Context, cityName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSlingResponse, error) { - rsp, err := c.PostV0CityByCityNameSlingWithBody(ctx, cityName, contentType, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameSlingWithBodyWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameSlingParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSlingResponse, error) { + rsp, err := c.PostV0CityByCityNameSlingWithBody(ctx, cityName, params, contentType, body, reqEditors...) if err != nil { return nil, err } return ParsePostV0CityByCityNameSlingResponse(rsp) } -func (c *ClientWithResponses) PostV0CityByCityNameSlingWithResponse(ctx context.Context, cityName string, body PostV0CityByCityNameSlingJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSlingResponse, error) { - rsp, err := c.PostV0CityByCityNameSling(ctx, cityName, body, reqEditors...) +func (c *ClientWithResponses) PostV0CityByCityNameSlingWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameSlingParams, body PostV0CityByCityNameSlingJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSlingResponse, error) { + rsp, err := c.PostV0CityByCityNameSling(ctx, cityName, params, body, reqEditors...) if err != nil { return nil, err } @@ -19745,6 +25019,15 @@ func (c *ClientWithResponses) GetV0CityByCityNameStatusWithResponse(ctx context. return ParseGetV0CityByCityNameStatusResponse(rsp) } +// PostV0CityByCityNameUnregisterWithResponse request returning *PostV0CityByCityNameUnregisterResponse +func (c *ClientWithResponses) PostV0CityByCityNameUnregisterWithResponse(ctx context.Context, cityName string, params *PostV0CityByCityNameUnregisterParams, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameUnregisterResponse, error) { + rsp, err := c.PostV0CityByCityNameUnregister(ctx, cityName, params, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostV0CityByCityNameUnregisterResponse(rsp) +} + // DeleteV0CityByCityNameWorkflowByWorkflowIdWithResponse request returning *DeleteV0CityByCityNameWorkflowByWorkflowIdResponse func (c *ClientWithResponses) DeleteV0CityByCityNameWorkflowByWorkflowIdWithResponse(ctx context.Context, cityName string, workflowId string, params *DeleteV0CityByCityNameWorkflowByWorkflowIdParams, reqEditors ...RequestEditorFn) (*DeleteV0CityByCityNameWorkflowByWorkflowIdResponse, error) { rsp, err := c.DeleteV0CityByCityNameWorkflowByWorkflowId(ctx, cityName, workflowId, params, reqEditors...) @@ -19879,12 +25162,12 @@ func ParsePostV0CityResponse(rsp *http.Response) (*PostV0CityResponse, error) { } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 202: var dest CityCreateResponse if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON200 = &dest + response.JSON202 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: var dest ErrorModel @@ -24259,6 +29542,39 @@ func ParseGetV0CityByCityNameStatusResponse(rsp *http.Response) (*GetV0CityByCit return response, nil } +// ParsePostV0CityByCityNameUnregisterResponse parses an HTTP response from a PostV0CityByCityNameUnregisterWithResponse call +func ParsePostV0CityByCityNameUnregisterResponse(rsp *http.Response) (*PostV0CityByCityNameUnregisterResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostV0CityByCityNameUnregisterResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 202: + var dest CityUnregisterResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON202 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest ErrorModel + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + // ParseDeleteV0CityByCityNameWorkflowByWorkflowIdResponse parses an HTTP response from a DeleteV0CityByCityNameWorkflowByWorkflowIdWithResponse call func ParseDeleteV0CityByCityNameWorkflowByWorkflowIdResponse(rsp *http.Response) (*DeleteV0CityByCityNameWorkflowByWorkflowIdResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/internal/api/genclient/doc.go b/internal/api/genclient/doc.go index 257ab6a001..9b1fef3812 100644 --- a/internal/api/genclient/doc.go +++ b/internal/api/genclient/doc.go @@ -2,7 +2,7 @@ // API. It is produced by `cmd/gen-client` from the live OpenAPI 3.0 // downgrade of the server's spec, processed through oapi-codegen v2.6.0. // -// See specs/architecture.md §2 "The generated Go client" for the +// See engdocs/architecture/api-control-plane.md §2 "The generated Go client" for the // three legitimate in-tree consumers of this package // (internal/api/client.go for CLI mutation coordination, // cmd/gc/cmd_events.go for direct event read/stream access, and diff --git a/internal/api/genclient_roundtrip_test.go b/internal/api/genclient_roundtrip_test.go index f7821a836d..8d684ef55b 100644 --- a/internal/api/genclient_roundtrip_test.go +++ b/internal/api/genclient_roundtrip_test.go @@ -194,7 +194,7 @@ func TestGenClientRoundTripConvoyCreate(t *testing.T) { Title: "round-trip convoy", Items: &items, } - resp, err := client.CreateConvoyWithResponse(context.Background(), state.CityName(), body) + resp, err := client.CreateConvoyWithResponse(context.Background(), state.CityName(), nil, body) if err != nil { t.Fatalf("CreateConvoy: %v", err) } diff --git a/internal/api/handler_beads.go b/internal/api/handler_beads.go index 56ca76c37b..e4141241d8 100644 --- a/internal/api/handler_beads.go +++ b/internal/api/handler_beads.go @@ -232,6 +232,99 @@ type BeadGraphResponse struct { Deps []workflowDepResponse `json:"deps"` } +func collectBeadGraph(store beads.Store, root beads.Bead) ([]beads.Bead, []workflowDepResponse, error) { + graphBeads := make([]beads.Bead, 0, 1) + beadIndex := make(map[string]beads.Bead) + + upsert := func(b beads.Bead) { + if b.ID == "" { + return + } + if existing, ok := beadIndex[b.ID]; ok { + if existing.ParentID == "" && b.ParentID != "" { + existing.ParentID = b.ParentID + beadIndex[b.ID] = existing + for i := range graphBeads { + if graphBeads[i].ID == b.ID { + graphBeads[i].ParentID = b.ParentID + break + } + } + } + return + } + beadIndex[b.ID] = b + graphBeads = append(graphBeads, b) + } + upsert(root) + + metadataChildren, err := store.List(beads.ListQuery{ + Metadata: map[string]string{"gc.root_bead_id": root.ID}, + IncludeClosed: true, + }) + if err != nil { + return nil, nil, fmt.Errorf("listing metadata children for bead %q: %w", root.ID, err) + } + for _, child := range metadataChildren { + upsert(child) + } + + parentEdges := make([]workflowDepResponse, 0) + seenEdges := make(map[string]bool) + addParentEdge := func(parentID, childID string) { + if parentID == "" || childID == "" { + return + } + edge := workflowDepResponse{From: parentID, To: childID, Kind: "parent-child"} + key := edge.From + "|" + edge.To + "|" + edge.Kind + if seenEdges[key] { + return + } + seenEdges[key] = true + parentEdges = append(parentEdges, edge) + } + + for i := 0; i < len(graphBeads); i++ { + parent := graphBeads[i] + children, err := store.List(beads.ListQuery{ + ParentID: parent.ID, + IncludeClosed: true, + Sort: beads.SortCreatedAsc, + }) + if err != nil { + return nil, nil, fmt.Errorf("listing child beads for bead %q: %w", parent.ID, err) + } + for _, child := range children { + if child.ParentID == "" { + child.ParentID = parent.ID + } + addParentEdge(parent.ID, child.ID) + upsert(child) + } + } + + return graphBeads, parentEdges, nil +} + +func mergeWorkflowDeps(primary, extra []workflowDepResponse) []workflowDepResponse { + if len(extra) == 0 { + return primary + } + seen := make(map[string]bool, len(primary)+len(extra)) + for _, edge := range primary { + seen[edge.From+"|"+edge.To+"|"+edge.Kind] = true + } + for _, edge := range extra { + key := edge.From + "|" + edge.To + "|" + edge.Kind + if seen[key] { + continue + } + primary = append(primary, edge) + seen[key] = true + } + return primary +} + // beadPrefix extracts the alphabetic prefix from a bead ID (e.g., "ga" from "ga-5b8i"). func beadPrefix(id string) string { for i, c := range id { diff --git a/internal/api/handler_beads_graph_test.go b/internal/api/handler_beads_graph_test.go index 035c497560..b69f01f5cd 100644 --- a/internal/api/handler_beads_graph_test.go +++ b/internal/api/handler_beads_graph_test.go @@ -2,6 +2,7 @@ package api import ( "encoding/json" + "errors" "net/http" "net/http/httptest" "testing" @@ -92,6 +93,88 @@ func TestBeadGraphReturnsRootAndChildren(t *testing.T) { } } +func TestBeadGraphIncludesParentChildChildrenAndEdges(t *testing.T) { + state := newFakeState(t) + store := state.stores["myrig"] + h := newTestCityHandler(t, state) + + root, err := store.Create(beads.Bead{Title: "Root", Type: "feature"}) + if err != nil { + t.Fatalf("Create(root): %v", err) + } + child, err := store.Create(beads.Bead{Title: "Child", Type: "task", ParentID: root.ID}) + if err != nil { + t.Fatalf("Create(child): %v", err) + } + sibling, err := store.Create(beads.Bead{Title: "Sibling", Type: "bug", ParentID: root.ID}) + if err != nil { + t.Fatalf("Create(sibling): %v", err) + } + + rec, resp := getGraph(t, h, state, root.ID) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want %d, body: %s", rec.Code, http.StatusOK, rec.Body.String()) + } + beadIDs := map[string]bool{} + for _, b := range resp.Beads { + beadIDs[b.ID] = true + } + for _, id := range []string{root.ID, child.ID, sibling.ID} { + if !beadIDs[id] { + t.Fatalf("graph beads missing %s; got %#v", id, resp.Beads) + } + } + + edges := map[string]bool{} + for _, dep := range resp.Deps { + edges[dep.From+"|"+dep.To+"|"+dep.Kind] = true + } + for _, id := range []string{child.ID, sibling.ID} { + key := root.ID + "|" + id + "|parent-child" + if !edges[key] { + t.Fatalf("graph deps missing %s; got %#v", key, resp.Deps) + } + } +} + +func TestBeadGraphReturnsErrorWhenGraphListFails(t *testing.T) { + state := newFakeState(t) + base := state.stores["myrig"] + root, err := base.Create(beads.Bead{Title: "Root", Type: "feature"}) + if err != nil { + t.Fatalf("Create(root): %v", err) + } + state.stores["myrig"] = &failingBeadStore{ + Store: base, + listErr: errors.New("list failed"), + } + h := newTestCityHandler(t, state) + + rec, _ := getGraph(t, h, state, root.ID) + + if rec.Code != http.StatusInternalServerError { + t.Fatalf("status = %d, want %d, body: %s", rec.Code, http.StatusInternalServerError, rec.Body.String()) + } +} + +func TestBeadGraphReturnsErrorWhenDepListFails(t *testing.T) { + state := newFakeState(t) + base := state.stores["myrig"] + root, err := base.Create(beads.Bead{Title: "Root", Type: "feature"}) + if err != nil { + t.Fatalf("Create(root): %v", err) + } + state.stores["myrig"] = depListFailStore{Store: base} + h := newTestCityHandler(t, state) + + rec, _ := getGraph(t, h, state, root.ID) + + if rec.Code != http.StatusInternalServerError { + t.Fatalf("status = %d, want %d, body: %s", rec.Code, http.StatusInternalServerError, rec.Body.String()) + } +} + func TestBeadGraphReturnsDeps(t *testing.T) { state := newFakeState(t) store := state.stores["myrig"] diff --git a/internal/api/handler_beads_test.go b/internal/api/handler_beads_test.go index 02924bb150..d90542d1a9 100644 --- a/internal/api/handler_beads_test.go +++ b/internal/api/handler_beads_test.go @@ -95,6 +95,28 @@ func (s *prefixedAliasStore) Create(b beads.Bead) (beads.Bead, error) { return s.beadToAlias(created), nil } +type sparseCreateStore struct { + *beads.MemStore +} + +func newSparseCreateStore() *sparseCreateStore { + return &sparseCreateStore{MemStore: beads.NewMemStore()} +} + +func (s *sparseCreateStore) Create(b beads.Bead) (beads.Bead, error) { + created, err := s.MemStore.Create(b) + if err != nil { + return beads.Bead{}, err + } + return beads.Bead{ + ID: created.ID, + Title: created.Title, + Type: created.Type, + Status: created.Status, + CreatedAt: created.CreatedAt, + }, nil +} + func (s *prefixedAliasStore) Get(id string) (beads.Bead, error) { s.getCalls++ b, err := s.base.Get(s.aliasToBase(id)) @@ -582,6 +604,97 @@ func TestBeadUpdate(t *testing.T) { } } +func TestBeadCreatePersistsMetadataAndParent(t *testing.T) { + state := newFakeState(t) + store := state.stores["myrig"] + parent, err := store.Create(beads.Bead{Title: "Parent"}) + if err != nil { + t.Fatalf("Create(parent): %v", err) + } + h := newTestCityHandler(t, state) + + body := `{ + "rig":"myrig", + "title":"Child", + "type":"feature", + "parent":"` + parent.ID + `", + "metadata":{ + "mc.contract.role":"child", + "mc.contract.run_id":"run-1" + } + }` + req := newPostRequest(cityURL(state, "/beads"), bytes.NewBufferString(body)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusCreated { + t.Fatalf("create status = %d, want %d, body: %s", rec.Code, http.StatusCreated, rec.Body.String()) + } + + var created beads.Bead + if err := json.NewDecoder(rec.Body).Decode(&created); err != nil { + t.Fatalf("decode created bead: %v", err) + } + if created.ParentID != parent.ID { + t.Fatalf("response parent = %q, want %q", created.ParentID, parent.ID) + } + if created.Metadata["mc.contract.run_id"] != "run-1" { + t.Fatalf("response metadata = %#v, want mc.contract.run_id=run-1", created.Metadata) + } + + got, err := store.Get(created.ID) + if err != nil { + t.Fatalf("Get(created): %v", err) + } + if got.ParentID != parent.ID { + t.Fatalf("stored parent = %q, want %q", got.ParentID, parent.ID) + } + if got.Metadata["mc.contract.role"] != "child" || got.Metadata["mc.contract.run_id"] != "run-1" { + t.Fatalf("stored metadata = %#v, want MC metadata", got.Metadata) + } +} + +func TestBeadCreateResponseUsesAuthoritativeStoredBead(t *testing.T) { + state := newFakeState(t) + store := newSparseCreateStore() + state.stores["myrig"] = store + parent, err := store.Create(beads.Bead{Title: "Parent"}) + if err != nil { + t.Fatalf("Create(parent): %v", err) + } + h := newTestCityHandler(t, state) + + body := `{ + "rig":"myrig", + "title":"Child", + "type":"feature", + "parent":"` + parent.ID + `", + "labels":["urgent"], + "metadata":{"mc.contract.run_id":"run-1"} + }` + req := newPostRequest(cityURL(state, "/beads"), bytes.NewBufferString(body)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusCreated { + t.Fatalf("create status = %d, want %d, body: %s", rec.Code, http.StatusCreated, rec.Body.String()) + } + + var created beads.Bead + if err := json.NewDecoder(rec.Body).Decode(&created); err != nil { + t.Fatalf("decode created bead: %v", err) + } + if created.ParentID != parent.ID { + t.Fatalf("response parent = %q, want %q", created.ParentID, parent.ID) + } + if len(created.Labels) != 1 || created.Labels[0] != "urgent" { + t.Fatalf("response labels = %#v, want [urgent]", created.Labels) + } + if created.Metadata["mc.contract.run_id"] != "run-1" { + t.Fatalf("response metadata = %#v, want mc.contract.run_id=run-1", created.Metadata) + } +} + func TestBeadUpdateUsesRoutePrefixStore(t *testing.T) { state, alphaStore, betaStore := configureBeadRouteState(t) created, err := betaStore.Create(beads.Bead{Title: "Routed beta bead"}) @@ -614,6 +727,51 @@ func TestBeadUpdateUsesRoutePrefixStore(t *testing.T) { } } +func TestBeadUpdateSetsAndClearsParent(t *testing.T) { + state := newFakeState(t) + store := state.stores["myrig"] + parent, err := store.Create(beads.Bead{Title: "Parent"}) + if err != nil { + t.Fatalf("Create(parent): %v", err) + } + child, err := store.Create(beads.Bead{Title: "Child"}) + if err != nil { + t.Fatalf("Create(child): %v", err) + } + h := newTestCityHandler(t, state) + + body := `{"parent":"` + parent.ID + `"}` + req := newPostRequest(cityURL(state, "/bead/")+child.ID+"/update", bytes.NewBufferString(body)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("set parent status = %d, want %d, body: %s", rec.Code, http.StatusOK, rec.Body.String()) + } + got, err := store.Get(child.ID) + if err != nil { + t.Fatalf("Get(child): %v", err) + } + if got.ParentID != parent.ID { + t.Fatalf("parent after set = %q, want %q", got.ParentID, parent.ID) + } + + req = newPostRequest(cityURL(state, "/bead/")+child.ID+"/update", bytes.NewBufferString(`{"parent":null}`)) + rec = httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("clear parent status = %d, want %d, body: %s", rec.Code, http.StatusOK, rec.Body.String()) + } + got, err = store.Get(child.ID) + if err != nil { + t.Fatalf("Get(child after clear): %v", err) + } + if got.ParentID != "" { + t.Fatalf("parent after clear = %q, want empty", got.ParentID) + } +} + func TestBeadDepsUsesRoutePrefixStore(t *testing.T) { state, alphaStore, betaStore := configureBeadRouteState(t) parent, err := betaStore.Create(beads.Bead{Title: "Parent"}) @@ -1169,6 +1327,50 @@ func TestBeadCreateValidation(t *testing.T) { } } +func TestBeadUpdateParentOpenAPISchemaAllowsNull(t *testing.T) { + data, err := os.ReadFile("openapi.json") + if err != nil { + t.Fatalf("read openapi.json: %v", err) + } + var spec map[string]any + if err := json.Unmarshal(data, &spec); err != nil { + t.Fatalf("parse openapi.json: %v", err) + } + components, ok := spec["components"].(map[string]any) + if !ok { + t.Fatal("openapi components missing") + } + schemas, ok := components["schemas"].(map[string]any) + if !ok { + t.Fatal("openapi schemas missing") + } + beadUpdate, ok := schemas["BeadUpdateBody"].(map[string]any) + if !ok { + t.Fatal("BeadUpdateBody schema missing") + } + properties, ok := beadUpdate["properties"].(map[string]any) + if !ok { + t.Fatal("BeadUpdateBody properties missing") + } + parent, ok := properties["parent"].(map[string]any) + if !ok { + t.Fatal("BeadUpdateBody parent property missing") + } + typeValues, ok := parent["type"].([]any) + if !ok { + t.Fatalf("parent type = %#v, want [\"string\", \"null\"]", parent["type"]) + } + seen := map[string]bool{} + for _, value := range typeValues { + if s, ok := value.(string); ok { + seen[s] = true + } + } + if !seen["string"] || !seen["null"] { + t.Fatalf("parent type = %#v, want string and null", parent["type"]) + } +} + func TestPackList(t *testing.T) { state := newFakeState(t) state.cfg.Packs = map[string]config.PackSource{ diff --git a/internal/api/handler_config.go b/internal/api/handler_config.go index eab75db547..21d6fb4787 100644 --- a/internal/api/handler_config.go +++ b/internal/api/handler_config.go @@ -45,7 +45,9 @@ type configRigResponse struct { type providerSpecJSON struct { DisplayName string `json:"display_name,omitempty"` Command string `json:"command,omitempty"` + ACPCommand string `json:"acp_command,omitempty"` Args []string `json:"args,omitempty"` + ACPArgs *[]string `json:"acp_args,omitempty"` PromptMode string `json:"prompt_mode,omitempty"` PromptFlag string `json:"prompt_flag,omitempty"` ReadyDelayMs int `json:"ready_delay_ms,omitempty"` diff --git a/internal/api/handler_config_test.go b/internal/api/handler_config_test.go index 1b6e8f66ec..060d9a7d77 100644 --- a/internal/api/handler_config_test.go +++ b/internal/api/handler_config_test.go @@ -16,7 +16,12 @@ func TestHandleConfigGet(t *testing.T) { fs.cfg.Agents[0].MinActiveSessions = intPtr(0) fs.cfg.Agents[0].MaxActiveSessions = intPtr(3) fs.cfg.Providers = map[string]config.ProviderSpec{ - "custom": {DisplayName: "Custom", Command: "custom-cli"}, + "custom": { + DisplayName: "Custom", + Command: "custom-cli", + ACPCommand: "custom-cli-acp", + ACPArgs: []string{"rpc", "--stdio"}, + }, } h := newTestCityHandler(t, fs) @@ -52,6 +57,12 @@ func TestHandleConfigGet(t *testing.T) { if _, ok := resp.Providers["custom"]; !ok { t.Error("expected 'custom' in providers") } + if resp.Providers["custom"].ACPCommand != "custom-cli-acp" { + t.Errorf("providers.custom.acp_command = %q, want %q", resp.Providers["custom"].ACPCommand, "custom-cli-acp") + } + if resp.Providers["custom"].ACPArgs == nil || len(*resp.Providers["custom"].ACPArgs) != 2 || (*resp.Providers["custom"].ACPArgs)[0] != "rpc" || (*resp.Providers["custom"].ACPArgs)[1] != "--stdio" { + t.Errorf("providers.custom.acp_args = %#v, want [rpc --stdio]", resp.Providers["custom"].ACPArgs) + } } func TestHandleConfigGet_UsesEffectiveWorkspaceIdentity(t *testing.T) { @@ -86,6 +97,46 @@ func TestHandleConfigGet_UsesEffectiveWorkspaceIdentity(t *testing.T) { } } +func TestHandleConfigGetPreservesExplicitEmptyACPArgs(t *testing.T) { + fs := newFakeState(t) + fs.cfg.Providers = map[string]config.ProviderSpec{ + "custom": { + Command: "custom-cli", + ACPCommand: "custom-cli-acp", + ACPArgs: []string{}, + }, + } + h := newTestCityHandler(t, fs) + + req := httptest.NewRequest("GET", cityURL(fs, "/config"), nil) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("status = %d, want %d; body = %s", w.Code, http.StatusOK, w.Body.String()) + } + + var resp map[string]any + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + providers, ok := resp["providers"].(map[string]any) + if !ok { + t.Fatal("expected providers map") + } + custom, ok := providers["custom"].(map[string]any) + if !ok { + t.Fatal("expected custom provider") + } + acpArgs, ok := custom["acp_args"].([]any) + if !ok { + t.Fatalf("acp_args = %#v, want empty array field", custom["acp_args"]) + } + if len(acpArgs) != 0 { + t.Fatalf("acp_args len = %d, want 0", len(acpArgs)) + } +} + func TestHandleConfigGet_DerivesPrefixFromRuntimeAliasWhenNoExplicitPrefix(t *testing.T) { fs := newFakeState(t) fs.cityName = "machine-alias" @@ -166,7 +217,12 @@ func TestHandleConfigExplain(t *testing.T) { fs.cfg.Agents[0].MinActiveSessions = intPtr(0) fs.cfg.Agents[0].MaxActiveSessions = intPtr(3) fs.cfg.Providers = map[string]config.ProviderSpec{ - "claude": {DisplayName: "My Claude", Command: "my-claude"}, + "claude": { + DisplayName: "My Claude", + Command: "my-claude", + ACPCommand: "my-claude-acp", + ACPArgs: []string{"rpc"}, + }, } h := newTestCityHandler(t, fs) @@ -206,6 +262,13 @@ func TestHandleConfigExplain(t *testing.T) { if claude["origin"] != "builtin+city" { t.Errorf("claude origin = %q, want %q", claude["origin"], "builtin+city") } + if claude["acp_command"] != "my-claude-acp" { + t.Errorf("claude acp_command = %q, want %q", claude["acp_command"], "my-claude-acp") + } + acpArgs, ok := claude["acp_args"].([]any) + if !ok || len(acpArgs) != 1 || acpArgs[0] != "rpc" { + t.Errorf("claude acp_args = %#v, want [rpc]", claude["acp_args"]) + } // A builtin-only provider should have origin "builtin". codex := providers["codex"].(map[string]any) if codex["origin"] != "builtin" { diff --git a/internal/api/handler_patches_test.go b/internal/api/handler_patches_test.go index 57cd216a17..fc91d91457 100644 --- a/internal/api/handler_patches_test.go +++ b/internal/api/handler_patches_test.go @@ -252,7 +252,7 @@ func TestHandleProviderPatchSet(t *testing.T) { fs := newFakeMutatorState(t) h := newTestCityHandler(t, fs) - body := `{"name":"claude","command":"my-claude"}` + body := `{"name":"claude","command":"my-claude","acp_command":"my-claude-acp","acp_args":["serve","--stdio"]}` req := httptest.NewRequest("PUT", cityURL(fs, "/patches/providers"), strings.NewReader(body)) req.Header.Set("X-GC-Request", "true") w := httptest.NewRecorder() @@ -265,6 +265,12 @@ func TestHandleProviderPatchSet(t *testing.T) { if len(fs.cfg.Patches.Providers) != 1 { t.Fatalf("patches.providers count = %d, want 1", len(fs.cfg.Patches.Providers)) } + if got := fs.cfg.Patches.Providers[0].ACPCommand; got == nil || *got != "my-claude-acp" { + t.Fatalf("ACPCommand = %v, want %q", got, "my-claude-acp") + } + if got := fs.cfg.Patches.Providers[0].ACPArgs; len(got) != 2 || got[0] != "serve" || got[1] != "--stdio" { + t.Fatalf("ACPArgs = %#v, want [\"serve\" \"--stdio\"]", got) + } } func TestHandleProviderPatchDelete(t *testing.T) { diff --git a/internal/api/handler_provider_crud_test.go b/internal/api/handler_provider_crud_test.go index 04ce338d08..ace9d75aec 100644 --- a/internal/api/handler_provider_crud_test.go +++ b/internal/api/handler_provider_crud_test.go @@ -1,10 +1,13 @@ package api import ( + "encoding/json" "net/http" "net/http/httptest" "strings" "testing" + + "github.com/gastownhall/gascity/internal/config" ) func TestHandleProviderCreate_AllowsBaseOnlyDescendant(t *testing.T) { @@ -32,6 +35,32 @@ func TestHandleProviderCreate_AllowsBaseOnlyDescendant(t *testing.T) { } } +func TestHandleProviderCreate_PersistsACPTransportOverrides(t *testing.T) { + fs := newFakeMutatorState(t) + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/providers"), strings.NewReader( + `{"name":"custom-acp","command":"custom","acp_command":"custom-acp","acp_args":["rpc","--stdio"]}`)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusCreated { + t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusCreated, rec.Body.String()) + } + + spec, ok := fs.cfg.Providers["custom-acp"] + if !ok { + t.Fatal("provider custom-acp not created") + } + if spec.ACPCommand != "custom-acp" { + t.Fatalf("ACPCommand = %q, want %q", spec.ACPCommand, "custom-acp") + } + if len(spec.ACPArgs) != 2 || spec.ACPArgs[0] != "rpc" || spec.ACPArgs[1] != "--stdio" { + t.Fatalf("ACPArgs = %#v, want [rpc --stdio]", spec.ACPArgs) + } +} + func TestHandleProviderUpdate_UpdatesInheritanceFields(t *testing.T) { fs := newFakeMutatorState(t) fs.cfg.Providers["custom"] = fs.cfg.Providers["test-agent"] @@ -58,3 +87,87 @@ func TestHandleProviderUpdate_UpdatesInheritanceFields(t *testing.T) { t.Fatalf("OptionsSchemaMerge = %q, want by_key", spec.OptionsSchemaMerge) } } + +func TestHandleProviderUpdate_UpdatesACPTransportOverrides(t *testing.T) { + fs := newFakeMutatorState(t) + fs.cfg.Providers["custom"] = fs.cfg.Providers["test-agent"] + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := httptest.NewRequest(http.MethodPatch, cityURL(fs, "/provider/custom"), strings.NewReader( + `{"acp_command":"custom-acp","acp_args":["rpc","--stdio"]}`)) + req.Header.Set("X-GC-Request", "true") + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusOK, rec.Body.String()) + } + + spec := fs.cfg.Providers["custom"] + if spec.ACPCommand != "custom-acp" { + t.Fatalf("ACPCommand = %q, want %q", spec.ACPCommand, "custom-acp") + } + if len(spec.ACPArgs) != 2 || spec.ACPArgs[0] != "rpc" || spec.ACPArgs[1] != "--stdio" { + t.Fatalf("ACPArgs = %#v, want [rpc --stdio]", spec.ACPArgs) + } +} + +func TestHandleProviderGet_IncludesACPTransportOverrides(t *testing.T) { + fs := newFakeState(t) + fs.cfg.Providers["custom"] = config.ProviderSpec{ + Command: "custom", + ACPCommand: "custom-acp", + ACPArgs: []string{"rpc", "--stdio"}, + } + h := newTestCityHandler(t, fs) + + req := httptest.NewRequest(http.MethodGet, cityURL(fs, "/provider/custom"), nil) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusOK, rec.Body.String()) + } + + var resp providerResponse + if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + if resp.ACPCommand != "custom-acp" { + t.Fatalf("ACPCommand = %q, want %q", resp.ACPCommand, "custom-acp") + } + if resp.ACPArgs == nil || len(*resp.ACPArgs) != 2 || (*resp.ACPArgs)[0] != "rpc" || (*resp.ACPArgs)[1] != "--stdio" { + t.Fatalf("ACPArgs = %#v, want [rpc --stdio]", resp.ACPArgs) + } +} + +func TestHandleProviderGetPreservesExplicitEmptyACPArgs(t *testing.T) { + fs := newFakeState(t) + fs.cfg.Providers["custom"] = config.ProviderSpec{ + Command: "custom", + ACPCommand: "custom-acp", + ACPArgs: []string{}, + } + h := newTestCityHandler(t, fs) + + req := httptest.NewRequest(http.MethodGet, cityURL(fs, "/provider/custom"), nil) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusOK, rec.Body.String()) + } + + var resp map[string]any + if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + acpArgs, ok := resp["acp_args"].([]any) + if !ok { + t.Fatalf("acp_args = %#v, want empty array field", resp["acp_args"]) + } + if len(acpArgs) != 0 { + t.Fatalf("acp_args len = %d, want 0", len(acpArgs)) + } +} diff --git a/internal/api/handler_providers.go b/internal/api/handler_providers.go index 6802256b5b..1891ada8fb 100644 --- a/internal/api/handler_providers.go +++ b/internal/api/handler_providers.go @@ -13,7 +13,9 @@ type providerResponse struct { Name string `json:"name"` DisplayName string `json:"display_name,omitempty"` Command string `json:"command,omitempty"` + ACPCommand string `json:"acp_command,omitempty"` Args []string `json:"args,omitempty"` + ACPArgs *[]string `json:"acp_args,omitempty"` PromptMode string `json:"prompt_mode,omitempty"` PromptFlag string `json:"prompt_flag,omitempty"` ReadyDelayMs int `json:"ready_delay_ms,omitempty"` @@ -40,7 +42,9 @@ func providerFromSpec(name string, spec config.ProviderSpec, builtin, cityLevel Name: name, DisplayName: spec.DisplayName, Command: spec.Command, + ACPCommand: spec.ACPCommand, Args: spec.Args, + ACPArgs: optionalStringSlice(spec.ACPArgs), PromptMode: spec.PromptMode, PromptFlag: spec.PromptFlag, ReadyDelayMs: spec.ReadyDelayMs, @@ -50,6 +54,15 @@ func providerFromSpec(name string, spec config.ProviderSpec, builtin, cityLevel } } +func optionalStringSlice(values []string) *[]string { + if values == nil { + return nil + } + cloned := make([]string, len(values)) + copy(cloned, values) + return &cloned +} + // toProviderPublicResponse builds the browser-safe DTO from a MERGED // provider spec. The spec must already be the result of // MergeProviderOverBuiltin so it carries the correct OptionsSchema and diff --git a/internal/api/handler_session_chat_test.go b/internal/api/handler_session_chat_test.go index bf1f1973a4..b429f0e6eb 100644 --- a/internal/api/handler_session_chat_test.go +++ b/internal/api/handler_session_chat_test.go @@ -1,9 +1,15 @@ package api import ( + "os" + "path/filepath" + "strings" "testing" + "github.com/gastownhall/gascity/internal/beads" "github.com/gastownhall/gascity/internal/config" + "github.com/gastownhall/gascity/internal/runtime" + sessionauto "github.com/gastownhall/gascity/internal/runtime/auto" "github.com/gastownhall/gascity/internal/session" "github.com/gastownhall/gascity/internal/shellquote" ) @@ -66,7 +72,10 @@ func TestBuildSessionResumeUsesResolvedProviderCommand(t *testing.T) { WorkDir: "/tmp/workdir", } - cmd, hints := srv.buildSessionResume(info) + cmd, hints, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } if got, want := cmd, "aimux run gemini -- --approval-mode yolo"; got != want { t.Fatalf("resume command = %q, want %q", got, want) } @@ -106,8 +115,653 @@ func TestBuildSessionResumePreservesStoredResolvedCommand(t *testing.T) { WorkDir: "/tmp/workdir", } - cmd, _ := srv.buildSessionResume(info) + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } if got, want := cmd, "claude --dangerously-skip-permissions --settings /tmp/settings.json"; got != want { t.Fatalf("resume command = %q, want %q", got, want) } } + +// TestBuildSessionResumeRebuildsBareStoredCommandForPoolClaudeAgent is a +// regression test for gastownhall/gascity#799: when a pool-agent session +// resumed through the control-dispatcher path has only the bare +// provider binary ("claude") as its stored command, the API must +// re-inject schema defaults (--dangerously-skip-permissions) and the +// provider-owned --settings path from the current resolved config. +// Before the fix, the bare stored command was preserved as-is and pool +// workers wedged on interactive permission prompts on resume. +func TestBuildSessionResumeRebuildsBareStoredCommandForPoolClaudeAgent(t *testing.T) { + fs := newSessionFakeState(t) + claude := config.BuiltinProviders()["claude"] + maxActive := 3 + gcDir := filepath.Join(fs.cityPath, ".gc") + if err := os.MkdirAll(gcDir, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(gcDir, "settings.json"), []byte(`{"hooks":{}}`), 0o644); err != nil { + t.Fatal(err) + } + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{ + { + Name: "perspective_planner", + Provider: "claude", + MaxActiveSessions: &maxActive, + }, + }, + Providers: map[string]config.ProviderSpec{ + "claude": claude, + }, + } + + srv := New(fs) + info := session.Info{ + ID: "gc-1", + Template: "perspective_planner", + Command: "claude", + Provider: "claude", + WorkDir: fs.cityPath, + SessionKey: "abc-123", + ResumeFlag: "--resume", + } + + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if !strings.Contains(cmd, "--dangerously-skip-permissions") { + t.Fatalf("resume command missing default args:\n got: %s", cmd) + } + if !strings.Contains(cmd, "--resume abc-123") { + t.Fatalf("resume command missing resume flag:\n got: %s", cmd) + } + if !strings.Contains(cmd, "--settings") { + t.Fatalf("resume command missing settings arg:\n got: %s", cmd) + } +} + +func TestBuildSessionResumeUsesStoredACPCommandForProviderSession(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Providers: map[string]config.ProviderSpec{ + "opencode": { + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + + state := &stateWithSessionProvider{ + fakeState: fs, + provider: sessionauto.New(runtime.NewFake(), runtime.NewFake()), + } + srv := New(state) + info := session.Info{ + ID: "gc-1", + Template: "opencode", + Command: "/bin/echo", + Provider: "opencode", + Transport: "acp", + WorkDir: "/tmp/workdir", + } + + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if got, want := cmd, "/bin/echo acp"; got != want { + t.Fatalf("resume command = %q, want %q", got, want) + } +} + +func TestBuildSessionResumeFallsBackToStoredCommandWhenTemplateOverridesInvalid(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Providers["test-agent"] = config.ProviderSpec{ + Command: "/bin/echo", + PathCheck: "true", + } + + info := createTestSession(t, fs.cityBeadStore, fs.sp, "Chat") + info.Template = "myrig/worker" + info.Command = "/bin/echo --stored" + if err := fs.cityBeadStore.SetMetadata(info.ID, "template_overrides", "{"); err != nil { + t.Fatalf("SetMetadata(template_overrides): %v", err) + } + + srv := New(fs) + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if got, want := cmd, "/bin/echo --stored"; got != want { + t.Fatalf("resume command = %q, want %q", got, want) + } +} + +func TestBuildSessionResumeUsesStoredACPCommandForLegacyProviderSessionWithoutTransportMetadataWithoutSessionAutoProvider(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Providers: map[string]config.ProviderSpec{ + "opencode": { + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + + srv := New(fs) + info := session.Info{ + ID: "gc-1", + Template: "opencode", + Command: "/bin/echo acp", + Provider: "opencode", + WorkDir: "/tmp/workdir", + } + + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if got, want := cmd, "/bin/echo acp"; got != want { + t.Fatalf("resume command = %q, want %q", got, want) + } +} + +func TestBuildSessionResumeUsesStoredACPCommandForLegacyProviderSessionWithoutTransportMetadata(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Providers: map[string]config.ProviderSpec{ + "opencode": { + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + + state := &stateWithSessionProvider{ + fakeState: fs, + provider: sessionauto.New(runtime.NewFake(), runtime.NewFake()), + } + srv := New(state) + info := session.Info{ + ID: "gc-1", + Template: "opencode", + Command: "/bin/echo acp", + Provider: "opencode", + WorkDir: "/tmp/workdir", + } + + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if got, want := cmd, "/bin/echo acp"; got != want { + t.Fatalf("resume command = %q, want %q", got, want) + } +} + +func TestBuildSessionResumeUsesStoredACPCommandForLegacyProviderSessionOnACPEnabledCustomProvider(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Providers: map[string]config.ProviderSpec{ + "custom-acp": { + DisplayName: "Custom ACP", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + + srv := New(fs) + info := session.Info{ + ID: "gc-1", + Template: "custom-acp", + Command: "/bin/echo acp", + Provider: "custom-acp", + WorkDir: "/tmp/workdir", + } + + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if got, want := cmd, "/bin/echo acp"; got != want { + t.Fatalf("resume command = %q, want %q", got, want) + } +} + +func TestBuildSessionResumeUsesStoredACPTransportForTemplateSession(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{ + {Name: "worker", Provider: "opencode", Session: "acp"}, + }, + Providers: map[string]config.ProviderSpec{ + "opencode": { + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + + srv := New(fs) + info := session.Info{ + ID: "gc-1", + Template: "worker", + Command: "/bin/echo", + Provider: "opencode", + Transport: "acp", + WorkDir: "/tmp/workdir", + } + + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if got, want := cmd, "/bin/echo acp"; got != want { + t.Fatalf("resume command = %q, want %q", got, want) + } +} + +func TestBuildSessionResumeDoesNotInferConfiguredACPTransportForTemplateSessionWithoutStoredMetadata(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{ + {Name: "worker", Provider: "opencode", Session: "acp"}, + }, + Providers: map[string]config.ProviderSpec{ + "opencode": { + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + + srv := New(fs) + info := session.Info{ + ID: "gc-1", + Template: "worker", + Command: "/bin/echo", + Provider: "opencode", + WorkDir: "/tmp/workdir", + } + + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if got, want := cmd, "/bin/echo"; got != want { + t.Fatalf("resume command = %q, want %q", got, want) + } +} + +func TestResolvedSessionTransportUsesResumeMetadataForLegacyACPWithSameCommand(t *testing.T) { + resolved := &config.ResolvedProvider{ + Command: "/bin/echo", + ACPCommand: "/bin/echo", + } + + got := resolvedSessionTransport(session.Info{ + Command: "/bin/echo", + }, resolved, "acp", map[string]string{ + "resume_flag": "--resume", + }, false) + if got != "acp" { + t.Fatalf("resolvedSessionTransport() = %q, want acp", got) + } +} + +func TestLegacyACPTransportAmbiguousWithSameCommand(t *testing.T) { + resolved := &config.ResolvedProvider{ + Command: "/bin/echo", + ACPCommand: "/bin/echo", + } + + if !legacyACPTransportAmbiguous(resolved, "acp", "/bin/echo", nil) { + t.Fatal("legacyACPTransportAmbiguous() = false, want true") + } +} + +func TestBuildSessionResumeUsesStartedConfigHashForLegacyProviderACPWithSameCommand(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Providers: map[string]config.ProviderSpec{ + "custom-acp": { + DisplayName: "Custom ACP", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + }, + }, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "identity.template.toml"), []byte(` +name = "identity" +command = "/bin/mcp" +args = ["{{.AgentName}}"] +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + + srv := New(fs) + resolved, err := srv.resolveBareProvider("custom-acp") + if err != nil { + t.Fatalf("resolveBareProvider: %v", err) + } + mcpServers, err := srv.sessionMCPServers("custom-acp", "custom-acp", "custom-acp", fs.cityPath, "acp", "provider") + if err != nil { + t.Fatalf("sessionMCPServers: %v", err) + } + startedHash := runtime.CoreFingerprint(runtime.Config{ + Command: resolved.ACPCommandString(), + Env: resolved.Env, + MCPServers: mcpServers, + }) + bead, err := fs.cityBeadStore.Create(beads.Bead{ + Type: "session", + Metadata: map[string]string{ + "mc_session_kind": "provider", + "started_config_hash": startedHash, + }, + }) + if err != nil { + t.Fatalf("Create(session bead): %v", err) + } + + _, hints, err := srv.buildSessionResume(session.Info{ + ID: bead.ID, + Template: "custom-acp", + Command: "/bin/echo", + Provider: "custom-acp", + WorkDir: fs.cityPath, + }) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if len(hints.MCPServers) != 1 { + t.Fatalf("len(hints.MCPServers) = %d, want 1", len(hints.MCPServers)) + } +} + +func TestBuildSessionResumeUsesStoredACPCommandForLegacyTemplateSessionWithoutTransportMetadata(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{ + {Name: "worker", Provider: "opencode", Session: "acp"}, + }, + Providers: map[string]config.ProviderSpec{ + "opencode": { + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + + srv := New(fs) + info := session.Info{ + ID: "gc-1", + Template: "worker", + Command: "/bin/echo acp", + Provider: "opencode", + WorkDir: "/tmp/workdir", + } + + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if got, want := cmd, "/bin/echo acp"; got != want { + t.Fatalf("resume command = %q, want %q", got, want) + } +} + +func TestBuildSessionResumeKeepsDefaultCommandForLegacyTemplateWithoutExplicitTransport(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{ + {Name: "worker", Provider: "opencode"}, + }, + Providers: map[string]config.ProviderSpec{ + "opencode": { + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + + srv := New(fs) + info := session.Info{ + ID: "gc-1", + Template: "worker", + Command: "/bin/echo", + Provider: "opencode", + WorkDir: "/tmp/workdir", + } + + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if got, want := cmd, "/bin/echo"; got != want { + t.Fatalf("resume command = %q, want %q", got, want) + } +} + +func TestBuildSessionResumeIgnoresMCPResolutionErrorForACPResume(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{ + {Name: "worker", Provider: "opencode", Session: "acp"}, + }, + Providers: map[string]config.ProviderSpec{ + "opencode": { + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "filesystem.toml"), []byte(` +name = "filesystem" +command = [broken +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + + srv := New(fs) + info := session.Info{ + ID: "gc-1", + Template: "worker", + Provider: "opencode", + Transport: "acp", + WorkDir: fs.cityPath, + } + + cmd, hints, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if got, want := cmd, "/bin/echo acp"; got != want { + t.Fatalf("resume command = %q, want %q", got, want) + } + if len(hints.MCPServers) != 0 { + t.Fatalf("Hints.MCPServers len = %d, want 0", len(hints.MCPServers)) + } +} + +func TestBuildSessionResumeIgnoresMCPResolutionErrorWithoutACPTransport(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{ + {Name: "worker", Provider: "stub"}, + }, + Providers: map[string]config.ProviderSpec{ + "stub": { + DisplayName: "Stub", + Command: "/bin/echo", + }, + }, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "filesystem.toml"), []byte(` +name = "filesystem" +command = [broken +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + + srv := New(fs) + info := session.Info{ + ID: "gc-1", + Template: "worker", + Provider: "stub", + WorkDir: fs.cityPath, + } + + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if got, want := cmd, "/bin/echo"; got != want { + t.Fatalf("resume command = %q, want %q", got, want) + } +} + +func TestBuildSessionResumeUsesStoredAgentNameForResumeMCPMaterialization(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{{ + Name: "ant", + Dir: "myrig", + Provider: "opencode", + Session: "acp", + WorkDir: ".gc/worktrees/{{.Rig}}/ants/{{.AgentBase}}", + MinActiveSessions: intPtr(0), + MaxActiveSessions: intPtr(4), + }}, + Providers: map[string]config.ProviderSpec{ + "opencode": { + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "identity.template.toml"), []byte(` +name = "identity" +command = "/bin/mcp" +args = ["{{.AgentName}}", "{{.WorkDir}}", "{{.TemplateName}}"] +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + + workDir := filepath.Join(fs.cityPath, ".gc", "worktrees", "myrig", "ants", "ant") + srv := New(fs) + info := session.Info{ + ID: "gc-1", + Template: "myrig/ant", + Alias: "ant", + AgentName: "myrig/ant-adhoc-123", + Provider: "opencode", + Transport: "acp", + WorkDir: workDir, + } + + cmd, hints, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if got, want := cmd, "/bin/echo acp"; got != want { + t.Fatalf("resume command = %q, want %q", got, want) + } + if len(hints.MCPServers) != 1 { + t.Fatalf("Hints.MCPServers len = %d, want 1", len(hints.MCPServers)) + } + if got, want := hints.MCPServers[0].Args[0], info.AgentName; got != want { + t.Fatalf("Args[0] = %q, want %q", got, want) + } + if got, want := hints.MCPServers[0].Args[1], workDir; got != want { + t.Fatalf("Args[1] = %q, want %q", got, want) + } + if got, want := hints.MCPServers[0].Args[2], "myrig/ant"; got != want { + t.Fatalf("Args[2] = %q, want %q", got, want) + } +} diff --git a/internal/api/handler_session_create.go b/internal/api/handler_session_create.go index 62826eefab..d71090d650 100644 --- a/internal/api/handler_session_create.go +++ b/internal/api/handler_session_create.go @@ -77,7 +77,7 @@ func (s *Server) handleSessionCreate(w http.ResponseWriter, r *http.Request) { switch kind { case "agent": var err error - resolved, workDir, transport, template, err = s.resolveSessionTemplate(name) + resolved, _, transport, template, err = s.resolveSessionTemplateForCreate(name) if err != nil { if errors.Is(err, errSessionTemplateNotFound) { s.idem.unreserve(idemKey) @@ -88,8 +88,14 @@ func (s *Server) handleSessionCreate(w http.ResponseWriter, r *http.Request) { writeError(w, http.StatusInternalServerError, "internal", err.Error()) return } - // Agent track: command comes from the agent config as-is. - // Do NOT inject OptionsSchema defaults — agents encode their own CLI flags. + transport, err = validateSessionTransport(resolved, transport, s.state.SessionProvider()) + if err != nil { + s.idem.unreserve(idemKey) + writeError(w, http.StatusServiceUnavailable, "provider_unavailable", err.Error()) + return + } + // Agent track stores a transport-aligned base command only. + // Do NOT inject OptionsSchema defaults or explicit overrides here. // Options are stored as template_overrides and applied at start time // by the session lifecycle via ResolveExplicitOptions. if len(body.Options) > 0 { @@ -126,8 +132,29 @@ func (s *Server) handleSessionCreate(w http.ResponseWriter, r *http.Request) { writeSessionManagerError(w, err) return } + createCtx, err := s.resolveAgentCreateContext(template, alias) + if err != nil { + s.idem.unreserve(idemKey) + writeError(w, http.StatusInternalServerError, "internal", err.Error()) + return + } + alias = createCtx.Alias + workDir = createCtx.WorkDir + + mcpServers, err := s.sessionMCPServers(template, resolved.Name, createCtx.Identity, workDir, transport, kind) + if err != nil { + s.idem.unreserve(idemKey) + writeError(w, http.StatusInternalServerError, "internal", err.Error()) + return + } - command := sessionCreateAgentCommand(resolved) + launchCommand, err := config.BuildProviderLaunchCommandWithoutOptions(s.state.CityPath(), resolved, transport) + if err != nil { + s.idem.unreserve(idemKey) + writeError(w, http.StatusInternalServerError, "internal", err.Error()) + return + } + command := launchCommand.Command // Build template_overrides metadata. Includes schema overrides AND // the initial message (as "initial_message" key). The reconciler @@ -137,13 +164,22 @@ func (s *Server) handleSessionCreate(w http.ResponseWriter, r *http.Request) { if extraMeta == nil { extraMeta = make(map[string]string) } + extraMeta["agent_name"] = createCtx.Identity extraMeta["session_origin"] = "ephemeral" + if transport == "acp" { + extraMeta, err = session.WithStoredMCPMetadata(extraMeta, createCtx.Identity, mcpServers) + if err != nil { + s.idem.unreserve(idemKey) + writeError(w, http.StatusInternalServerError, "internal", err.Error()) + return + } + } // Agent sessions always use async (bead-only) creation. The reconciler // starts the agent process on the next tick. This avoids blocking the // HTTP response for 10-30s while the agent boots in tmux, and lets MC // show the session in the sidebar immediately via optimistic UI. - resolvedCfg, err := resolvedSessionConfigForProvider(alias, "", template, title, transport, extraMeta, resolved, command, workDir) + resolvedCfg, err := resolvedSessionConfigForProvider(alias, createCtx.ExplicitName, template, title, transport, extraMeta, resolved, command, workDir, mcpServers) if err != nil { s.idem.unreserve(idemKey) writeSessionManagerError(w, err) @@ -156,10 +192,23 @@ func (s *Server) handleSessionCreate(w http.ResponseWriter, r *http.Request) { return } var info session.Info - err = session.WithCitySessionAliasLock(s.state.CityPath(), alias, func() error { + reservationIDs := []string{alias, createCtx.ExplicitName} + reserveConcreteIdentity := createCtx.Agent.SupportsMultipleSessions() && strings.TrimSpace(createCtx.Identity) != "" + if reserveConcreteIdentity { + reservationIDs = append(reservationIDs, createCtx.Identity) + } + err = session.WithCitySessionIdentifierLocks(s.state.CityPath(), reservationIDs, func() error { if err := session.EnsureAliasAvailableWithConfig(store, s.state.Config(), alias, ""); err != nil { return err } + if reserveConcreteIdentity && createCtx.Identity != alias { + if err := session.EnsureAliasAvailableWithConfig(store, s.state.Config(), createCtx.Identity, ""); err != nil { + return err + } + } + if err := session.EnsureSessionNameAvailableWithConfig(store, s.state.Config(), createCtx.ExplicitName, ""); err != nil { + return err + } var createErr error info, createErr = handle.Create(r.Context(), worker.CreateModeDeferred) return createErr @@ -273,8 +322,20 @@ func (s *Server) createProviderSession(w http.ResponseWriter, r *http.Request, s writeSessionManagerError(w, err) return } + mcpIdentity, err := providerSessionMCPIdentity(providerName, alias) + if err != nil { + s.idem.unreserve(idemKey) + writeError(w, http.StatusInternalServerError, "internal", err.Error()) + return + } - launchCommand, err := config.BuildProviderLaunchCommand(s.state.CityPath(), resolved, body.Options) + transport, err := providerSessionTransport(resolved, s.state.SessionProvider()) + if err != nil { + s.idem.unreserve(idemKey) + writeError(w, http.StatusServiceUnavailable, "provider_unavailable", err.Error()) + return + } + launchCommand, err := config.BuildProviderLaunchCommand(s.state.CityPath(), resolved, body.Options, transport) if err != nil { s.idem.unreserve(idemKey) if errors.Is(err, config.ErrUnknownOption) { @@ -285,10 +346,25 @@ func (s *Server) createProviderSession(w http.ResponseWriter, r *http.Request, s return } command := launchCommand.Command - - resolvedCfg, err := resolvedSessionConfigForProvider(alias, "", template, title, "", map[string]string{ + mcpServers, err := s.providerSessionMCPServers(providerName, mcpIdentity, workDir, transport) + if err != nil { + s.idem.unreserve(idemKey) + writeError(w, http.StatusInternalServerError, "internal", err.Error()) + return + } + extraMeta := map[string]string{ "session_origin": "manual", - }, resolved, command, workDir) + } + if transport == "acp" { + extraMeta, err = session.WithStoredMCPMetadata(extraMeta, mcpIdentity, mcpServers) + if err != nil { + s.idem.unreserve(idemKey) + writeError(w, http.StatusInternalServerError, "internal", err.Error()) + return + } + } + + resolvedCfg, err := resolvedSessionConfigForProvider(alias, "", template, title, transport, extraMeta, resolved, command, workDir, mcpServers) if err != nil { s.idem.unreserve(idemKey) writeSessionManagerError(w, err) @@ -359,10 +435,6 @@ func (s *Server) createProviderSession(w http.ResponseWriter, r *http.Request, s writeJSON(w, statusCode, resp) } -func sessionCreateAgentCommand(resolved *config.ResolvedProvider) string { - return firstNonEmptyString(resolved.CommandString(), resolved.Name) -} - func sessionTemplateOverridesMetadata(options map[string]string, message string) map[string]string { allOverrides := make(map[string]string, len(options)+1) for k, v := range options { diff --git a/internal/api/handler_sessions_test.go b/internal/api/handler_sessions_test.go index 83cb25056a..1eb6ba7503 100644 --- a/internal/api/handler_sessions_test.go +++ b/internal/api/handler_sessions_test.go @@ -19,6 +19,7 @@ import ( "github.com/gastownhall/gascity/internal/config" "github.com/gastownhall/gascity/internal/events" "github.com/gastownhall/gascity/internal/runtime" + sessionauto "github.com/gastownhall/gascity/internal/runtime/auto" "github.com/gastownhall/gascity/internal/session" "github.com/gastownhall/gascity/internal/sessionlog" "github.com/gastownhall/gascity/internal/worker" @@ -76,6 +77,14 @@ func (p *failNudgeProvider) Nudge(name string, content []runtime.ContentBlock) e return nil } +type transportCapableProvider struct { + *runtime.Fake +} + +func (p *transportCapableProvider) SupportsTransport(transport string) bool { + return transport == "acp" +} + type stateWithSessionProvider struct { *fakeState provider runtime.Provider @@ -1125,6 +1134,16 @@ func TestHandleSessionCreate(t *testing.T) { if resp.Title != "myrig/worker" { t.Errorf("Title = %q, want default %q", resp.Title, "myrig/worker") } + bead, err := fs.cityBeadStore.Get(resp.ID) + if err != nil { + t.Fatalf("Get(%s): %v", resp.ID, err) + } + if got := bead.Metadata[session.MCPIdentityMetadataKey]; got != "" { + t.Fatalf("mcp_identity = %q, want empty for non-ACP agent session", got) + } + if got := bead.Metadata[session.MCPServersSnapshotMetadataKey]; got != "" { + t.Fatalf("mcp_servers_snapshot = %q, want empty for non-ACP agent session", got) + } // Agent sessions are always created async — not running until the // reconciler starts the process. if resp.Running { @@ -1135,6 +1154,242 @@ func TestHandleSessionCreate(t *testing.T) { } } +func TestHandleSessionCreateUsesACPTransportCommandForAgentTemplate(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Agents[0].Provider = "opencode" + fs.cfg.Agents[0].Session = "acp" + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + state := &stateWithSessionProvider{ + fakeState: fs, + provider: &transportCapableProvider{Fake: runtime.NewFake()}, + } + srv := New(state) + h := newTestCityHandlerWith(t, state, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"agent","name":"myrig/worker"}`)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusAccepted { + t.Fatalf("status = %d, want %d; body: %s", rec.Code, http.StatusAccepted, rec.Body.String()) + } + + var resp sessionResponse + if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { + t.Fatalf("decode: %v", err) + } + bead, err := state.cityBeadStore.Get(resp.ID) + if err != nil { + t.Fatalf("Get(%s): %v", resp.ID, err) + } + if got, want := bead.Metadata["command"], "/bin/echo acp"; got != want { + t.Fatalf("command metadata = %q, want %q", got, want) + } + if got, want := bead.Metadata["transport"], "acp"; got != want { + t.Fatalf("transport metadata = %q, want %q", got, want) + } +} + +func TestHumaHandleSessionCreateUsesACPTransportCommandForAgentTemplate(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Agents[0].Provider = "opencode" + fs.cfg.Agents[0].Session = "acp" + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + state := &stateWithSessionProvider{ + fakeState: fs, + provider: &transportCapableProvider{Fake: runtime.NewFake()}, + } + srv := New(state) + + out, err := srv.humaHandleSessionCreate(context.Background(), &SessionCreateInput{ + Body: sessionCreateBody{ + Kind: "agent", + Name: "myrig/worker", + }, + }) + if err != nil { + t.Fatalf("humaHandleSessionCreate: %v", err) + } + if got, want := out.Status, http.StatusAccepted; got != want { + t.Fatalf("status = %d, want %d", got, want) + } + bead, err := state.cityBeadStore.Get(out.Body.ID) + if err != nil { + t.Fatalf("Get(%s): %v", out.Body.ID, err) + } + if got, want := bead.Metadata["command"], "/bin/echo acp"; got != want { + t.Fatalf("command metadata = %q, want %q", got, want) + } + if got, want := bead.Metadata["transport"], "acp"; got != want { + t.Fatalf("transport metadata = %q, want %q", got, want) + } +} + +func TestHandleSessionCreateRejectsACPAgentWithoutACPRouting(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Agents[0].Provider = "opencode" + fs.cfg.Agents[0].Session = "acp" + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"agent","name":"myrig/worker"}`)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusServiceUnavailable { + t.Fatalf("status = %d, want %d; body: %s", rec.Code, http.StatusServiceUnavailable, rec.Body.String()) + } + if !strings.Contains(rec.Body.String(), "requires ACP transport") { + t.Fatalf("body = %q, want ACP transport error", rec.Body.String()) + } +} + +func TestHumaHandleSessionCreateRejectsACPAgentWithoutACPRouting(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Agents[0].Provider = "opencode" + fs.cfg.Agents[0].Session = "acp" + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + srv := New(fs) + + if _, err := srv.humaHandleSessionCreate(context.Background(), &SessionCreateInput{ + Body: sessionCreateBody{ + Kind: "agent", + Name: "myrig/worker", + }, + }); err == nil { + t.Fatal("humaHandleSessionCreate() error = nil, want ACP routing error") + } else if !strings.Contains(err.Error(), "requires ACP transport") { + t.Fatalf("humaHandleSessionCreate() error = %v, want ACP transport error", err) + } +} + +func TestHandleSessionCreateRejectsACPAgentWhenProviderLacksACP(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Agents[0].Provider = "custom" + fs.cfg.Agents[0].Session = "acp" + fs.cfg.Providers["custom"] = config.ProviderSpec{ + DisplayName: "Custom", + Command: "/bin/echo", + PathCheck: "true", + } + state := &stateWithSessionProvider{ + fakeState: fs, + provider: &transportCapableProvider{Fake: runtime.NewFake()}, + } + srv := New(state) + h := newTestCityHandlerWith(t, state, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"agent","name":"myrig/worker"}`)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusServiceUnavailable { + t.Fatalf("status = %d, want %d; body: %s", rec.Code, http.StatusServiceUnavailable, rec.Body.String()) + } + if !strings.Contains(rec.Body.String(), "does not support ACP transport") { + t.Fatalf("body = %q, want provider ACP support error", rec.Body.String()) + } +} + +func TestHumaHandleSessionCreatePropagatesMCPResolutionErrorForACPAgent(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Agents[0].Provider = "opencode" + fs.cfg.Agents[0].Session = "acp" + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "filesystem.toml"), []byte(` +name = "filesystem" +command = [broken +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + state := &stateWithSessionProvider{ + fakeState: fs, + provider: &transportCapableProvider{Fake: runtime.NewFake()}, + } + srv := New(state) + + if _, err := srv.humaHandleSessionCreate(context.Background(), &SessionCreateInput{ + Body: sessionCreateBody{ + Kind: "agent", + Name: "myrig/worker", + }, + }); err == nil { + t.Fatal("humaHandleSessionCreate() error = nil, want MCP resolution error") + } else if !strings.Contains(err.Error(), "loading effective MCP") { + t.Fatalf("humaHandleSessionCreate() error = %v, want MCP resolution error", err) + } +} + +func TestHandleSessionCreateIgnoresBrokenMCPWithoutACPTransport(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "filesystem.toml"), []byte(` +name = "filesystem" +command = [broken +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"agent","name":"myrig/worker"}`)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusAccepted { + t.Fatalf("status = %d, want %d; body: %s", rec.Code, http.StatusAccepted, rec.Body.String()) + } +} + func TestHandleSessionCreateAsync(t *testing.T) { fs := newSessionFakeState(t) srv := New(fs) @@ -1247,6 +1502,62 @@ func TestHandleSessionCreateAsync_PoolTemplateWithoutAliasUsesGeneratedWorkDirId } } +func TestResolveAgentCreateContextUsesConcreteIdentityForMCPMaterialization(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Agents = []config.Agent{{ + Name: "ant", + Dir: "myrig", + Provider: "opencode", + Session: "acp", + WorkDir: ".gc/worktrees/{{.Rig}}/ants/{{.AgentBase}}", + MinActiveSessions: intPtr(0), + MaxActiveSessions: intPtr(4), + }} + fs.cfg.NamedSessions = nil + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "identity.template.toml"), []byte(` +name = "identity" +command = "/bin/mcp" +args = ["{{.AgentName}}", "{{.WorkDir}}", "{{.TemplateName}}"] +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + + srv := New(fs) + createCtx, err := srv.resolveAgentCreateContext("myrig/ant", "") + if err != nil { + t.Fatalf("resolveAgentCreateContext: %v", err) + } + mcpServers, err := srv.sessionMCPServers("myrig/ant", "opencode", createCtx.Identity, createCtx.WorkDir, "acp", "agent") + if err != nil { + t.Fatalf("sessionMCPServers: %v", err) + } + if len(mcpServers) != 1 { + t.Fatalf("len(mcpServers) = %d, want 1", len(mcpServers)) + } + if got, want := mcpServers[0].Args[0], createCtx.Identity; got != want { + t.Fatalf("Args[0] = %q, want %q", got, want) + } + if got, want := mcpServers[0].Args[1], createCtx.WorkDir; got != want { + t.Fatalf("Args[1] = %q, want %q", got, want) + } + if got, want := mcpServers[0].Args[2], "myrig/ant"; got != want { + t.Fatalf("Args[2] = %q, want %q", got, want) + } +} + func TestHandleSessionCreateAsync_PoolTemplateCanonicalizesAliasCollisions(t *testing.T) { fs := newSessionFakeState(t) fs.cfg.Agents = []config.Agent{{ @@ -1391,6 +1702,95 @@ func TestMaterializeNamedSessionStampsProviderFamilyMetadata(t *testing.T) { } } +func TestMaterializeNamedSessionRejectsACPTemplateWithoutACPRouting(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Agents[0].Provider = "opencode" + fs.cfg.Agents[0].Session = "acp" + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + srv := New(fs) + + spec, ok, err := srv.findNamedSessionSpecForTarget(fs.cityBeadStore, "worker") + if err != nil { + t.Fatalf("findNamedSessionSpecForTarget: %v", err) + } + if !ok { + t.Fatal("expected named session spec") + } + if _, err := srv.materializeNamedSession(fs.cityBeadStore, spec); err == nil { + t.Fatal("materializeNamedSession() error = nil, want ACP routing error") + } else if !strings.Contains(err.Error(), "requires ACP transport") { + t.Fatalf("materializeNamedSession() error = %v, want ACP transport error", err) + } + items, err := fs.cityBeadStore.ListByLabel(session.LabelSession, 0) + if err != nil { + t.Fatalf("ListByLabel: %v", err) + } + if len(items) != 0 { + t.Fatalf("session bead count = %d, want 0", len(items)) + } +} + +func TestMaterializeNamedSessionPersistsStoredMCPMetadata(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Agents[0].Provider = "opencode" + fs.cfg.Agents[0].Session = "acp" + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "identity.template.toml"), []byte(` +name = "identity" +command = "/bin/mcp" +args = ["{{.AgentName}}", "{{.WorkDir}}", "{{.TemplateName}}"] +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + state := &stateWithSessionProvider{ + fakeState: fs, + provider: &transportCapableProvider{Fake: runtime.NewFake()}, + } + srv := New(state) + + spec, ok, err := srv.findNamedSessionSpecForTarget(fs.cityBeadStore, "worker") + if err != nil { + t.Fatalf("findNamedSessionSpecForTarget: %v", err) + } + if !ok { + t.Fatal("expected named session spec") + } + id, err := srv.materializeNamedSession(fs.cityBeadStore, spec) + if err != nil { + t.Fatalf("materializeNamedSession: %v", err) + } + bead, err := fs.cityBeadStore.Get(id) + if err != nil { + t.Fatalf("Get(%s): %v", id, err) + } + if got, want := bead.Metadata[session.MCPIdentityMetadataKey], spec.Identity; got != want { + t.Fatalf("mcp_identity = %q, want %q", got, want) + } + if got := bead.Metadata[session.MCPServersSnapshotMetadataKey]; got == "" { + t.Fatal("mcp_servers_snapshot = empty, want persisted snapshot") + } +} + func TestHandleProviderSessionCreateWithMessageUsesProviderDefaultNudge(t *testing.T) { fs := newSessionFakeState(t) srv := New(fs) @@ -1431,6 +1831,269 @@ func TestHandleProviderSessionCreateWithMessageUsesProviderDefaultNudge(t *testi } } +func TestHandleProviderSessionCreateUsesACPTransportCommand(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + defaultSP := runtime.NewFake() + acpSP := &transportCapableProvider{Fake: runtime.NewFake()} + state := &stateWithSessionProvider{ + fakeState: fs, + provider: sessionauto.New(defaultSP, acpSP), + } + srv := New(state) + h := newTestCityHandlerWith(t, state, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"provider","name":"opencode"}`)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusCreated { + t.Fatalf("status = %d, want %d; body: %s", rec.Code, http.StatusCreated, rec.Body.String()) + } + + var resp sessionResponse + if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { + t.Fatalf("decode: %v", err) + } + start := acpSP.LastStartConfig(resp.SessionName) + if start == nil { + t.Fatalf("LastStartConfig(%q) = nil", resp.SessionName) + } + if got, want := start.Command, "/bin/echo acp"; got != want { + t.Fatalf("start command = %q, want %q", got, want) + } + bead, err := fs.cityBeadStore.Get(resp.ID) + if err != nil { + t.Fatalf("Get(%s): %v", resp.ID, err) + } + if got, want := bead.Metadata["transport"], "acp"; got != want { + t.Fatalf("transport metadata = %q, want %q", got, want) + } + if defaultSP.IsRunning(resp.SessionName) { + t.Fatalf("default backend should not own ACP session %q", resp.SessionName) + } +} + +func TestHumaCreateProviderSessionUsesACPTransportCommand(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + defaultSP := runtime.NewFake() + acpSP := &transportCapableProvider{Fake: runtime.NewFake()} + state := &stateWithSessionProvider{ + fakeState: fs, + provider: sessionauto.New(defaultSP, acpSP), + } + srv := New(state) + + out, err := srv.humaCreateProviderSession(context.Background(), fs.cityBeadStore, sessionCreateBody{ + Kind: "provider", + Name: "opencode", + }, "opencode") + if err != nil { + t.Fatalf("humaCreateProviderSession: %v", err) + } + if got, want := out.Status, http.StatusCreated; got != want { + t.Fatalf("status = %d, want %d", got, want) + } + start := acpSP.LastStartConfig(out.Body.SessionName) + if start == nil { + t.Fatalf("LastStartConfig(%q) = nil", out.Body.SessionName) + } + if got, want := start.Command, "/bin/echo acp"; got != want { + t.Fatalf("start command = %q, want %q", got, want) + } + bead, err := fs.cityBeadStore.Get(out.Body.ID) + if err != nil { + t.Fatalf("Get(%s): %v", out.Body.ID, err) + } + if got, want := bead.Metadata["transport"], "acp"; got != want { + t.Fatalf("transport metadata = %q, want %q", got, want) + } + if defaultSP.IsRunning(out.Body.SessionName) { + t.Fatalf("default backend should not own ACP session %q", out.Body.SessionName) + } +} + +func TestHandleProviderSessionCreateUsesACPTransportCapabilityProvider(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + provider := &transportCapableProvider{Fake: runtime.NewFake()} + state := &stateWithSessionProvider{ + fakeState: fs, + provider: provider, + } + srv := New(state) + h := newTestCityHandlerWith(t, state, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"provider","name":"opencode"}`)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusCreated { + t.Fatalf("status = %d, want %d; body: %s", rec.Code, http.StatusCreated, rec.Body.String()) + } + + var resp sessionResponse + if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { + t.Fatalf("decode: %v", err) + } + start := provider.LastStartConfig(resp.SessionName) + if start == nil { + t.Fatalf("LastStartConfig(%q) = nil", resp.SessionName) + } + if got, want := start.Command, "/bin/echo acp"; got != want { + t.Fatalf("start command = %q, want %q", got, want) + } + bead, err := fs.cityBeadStore.Get(resp.ID) + if err != nil { + t.Fatalf("Get(%s): %v", resp.ID, err) + } + if got, want := bead.Metadata["transport"], "acp"; got != want { + t.Fatalf("transport metadata = %q, want %q", got, want) + } +} + +func TestHandleProviderSessionCreateUsesPerSessionMCPIdentity(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "identity.template.toml"), []byte(` +name = "identity" +command = "/bin/mcp" +args = ["{{.AgentName}}", "{{.WorkDir}}", "{{.TemplateName}}"] +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + provider := &transportCapableProvider{Fake: runtime.NewFake()} + state := &stateWithSessionProvider{ + fakeState: fs, + provider: provider, + } + srv := New(state) + h := newTestCityHandlerWith(t, state, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"provider","name":"opencode"}`)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusCreated { + t.Fatalf("status = %d, want %d; body: %s", rec.Code, http.StatusCreated, rec.Body.String()) + } + + var resp sessionResponse + if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { + t.Fatalf("decode: %v", err) + } + start := provider.LastStartConfig(resp.SessionName) + if start == nil { + t.Fatalf("LastStartConfig(%q) = nil", resp.SessionName) + } + if len(start.MCPServers) != 1 { + t.Fatalf("Start MCPServers len = %d, want 1", len(start.MCPServers)) + } + bead, err := fs.cityBeadStore.Get(resp.ID) + if err != nil { + t.Fatalf("Get(%s): %v", resp.ID, err) + } + if got := bead.Metadata[session.MCPIdentityMetadataKey]; got == "" { + t.Fatal("mcp_identity metadata = empty, want per-session identity") + } + if got, want := start.MCPServers[0].Args[0], bead.Metadata[session.MCPIdentityMetadataKey]; got != want { + t.Fatalf("Start MCP identity = %q, want %q", got, want) + } + if got := bead.Metadata[session.MCPIdentityMetadataKey]; got == "opencode" { + t.Fatalf("mcp_identity metadata = %q, want unique per-session identity", got) + } + if got, want := start.MCPServers[0].Args[1], fs.cityPath; got != want { + t.Fatalf("Start workdir arg = %q, want %q", got, want) + } + if got, want := start.MCPServers[0].Args[2], bead.Metadata[session.MCPIdentityMetadataKey]; got != want { + t.Fatalf("Start template arg = %q, want %q", got, want) + } +} + +func TestHandleProviderSessionCreateRejectsACPProviderWithoutACPRouting(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"provider","name":"opencode"}`)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusServiceUnavailable { + t.Fatalf("status = %d, want %d; body: %s", rec.Code, http.StatusServiceUnavailable, rec.Body.String()) + } + if !strings.Contains(rec.Body.String(), "requires ACP transport") { + t.Fatalf("body = %q, want ACP transport error", rec.Body.String()) + } +} + +func TestHumaCreateProviderSessionRejectsACPProviderWithoutACPRouting(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Providers["opencode"] = config.ProviderSpec{ + DisplayName: "OpenCode", + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + srv := New(fs) + + if _, err := srv.humaCreateProviderSession(context.Background(), fs.cityBeadStore, sessionCreateBody{ + Kind: "provider", + Name: "opencode", + }, "opencode"); err == nil { + t.Fatal("humaCreateProviderSession() error = nil, want ACP routing error") + } +} + func TestHandleProviderSessionCreateWithMessageRollsBackOnDeliveryFailure(t *testing.T) { fs := newSessionFakeState(t) provider := &failNudgeProvider{Fake: runtime.NewFake(), err: errors.New("nudge failed")} diff --git a/internal/api/handler_sling.go b/internal/api/handler_sling.go index cfa21b17a8..5c1e3acc3b 100644 --- a/internal/api/handler_sling.go +++ b/internal/api/handler_sling.go @@ -65,16 +65,26 @@ func (s *Server) execSling(ctx context.Context, body slingBody, _ string) (*slin formulaName := strings.TrimSpace(body.Formula) attachedBeadID := strings.TrimSpace(body.AttachedBeadID) + storeBeadID := slingStoreBeadID(body) // Build deps and construct Sling instance. - store := s.findSlingStore(body.Rig, agentCfg) + store := s.findSlingStore(body.Rig, agentCfg, storeBeadID) + storeRef := s.slingStoreRef(body.Rig, agentCfg, storeBeadID) + if store == nil && allowsForceStoreFallback(body, agentCfg) { + store = s.findSlingStore(body.Rig, agentCfg, "") + storeRef = s.slingStoreRef(body.Rig, agentCfg, "") + } + if store == nil { + message := fmt.Sprintf("bead prefix store %s is not registered; cannot verify bead %q", storeRef, storeBeadID) + return nil, http.StatusBadRequest, "missing_bead", message, nil + } deps := sling.SlingDeps{ CityName: s.state.CityName(), CityPath: s.state.CityPath(), Cfg: s.state.Config(), SP: s.state.SessionProvider(), Store: store, - StoreRef: s.slingStoreRef(body.Rig, agentCfg), + StoreRef: storeRef, SourceWorkflowStores: func() ([]sling.SourceWorkflowStore, error) { return s.sourceWorkflowStores(), nil }, @@ -145,6 +155,19 @@ func (s *Server) execSling(ctx context.Context, body slingBody, _ string) (*slin if errors.As(err, &conflictErr) { return nil, http.StatusConflict, "conflict", err.Error(), conflictErr } + var lookupErr *sling.BeadLookupError + if errors.As(err, &lookupErr) { + fmt.Fprintf(apiSlingStderr(), "gc api sling: %v\n", lookupErr) //nolint:errcheck + return nil, http.StatusInternalServerError, "internal", "sling bead lookup failed", nil + } + var missingBeadErr *sling.MissingBeadError + if errors.As(err, &missingBeadErr) { + return nil, http.StatusBadRequest, "missing_bead", err.Error(), nil + } + var crossRigErr *sling.CrossRigError + if errors.As(err, &crossRigErr) { + return nil, http.StatusBadRequest, "cross_rig", err.Error(), nil + } return nil, http.StatusBadRequest, "invalid", err.Error(), nil } @@ -170,6 +193,24 @@ func (s *Server) execSling(ctx context.Context, body slingBody, _ string) (*slin return resp, http.StatusCreated, "", "", nil } +func allowsForceStoreFallback(body slingBody, agentCfg config.Agent) bool { + if !body.Force || strings.TrimSpace(body.Bead) == "" { + return false + } + if strings.TrimSpace(body.Formula) != "" || strings.TrimSpace(body.AttachedBeadID) != "" { + return false + } + return agentCfg.EffectiveDefaultSlingFormula() == "" +} + +func slingStoreBeadID(body slingBody) string { + // Formula attachment validates the attached bead, not the formula name. + if attachedBeadID := strings.TrimSpace(body.AttachedBeadID); attachedBeadID != "" { + return attachedBeadID + } + return strings.TrimSpace(body.Bead) +} + // sourceWorkflowCleanupHint renders the CLI command that clears the blocking // source workflow. Surfaced in the conflict response body so users can fix // the state without grepping docs. @@ -183,7 +224,14 @@ func sourceWorkflowCleanupHint(sourceBeadID, storeRef string) string { } // findSlingStore returns the bead store for sling operations. -func (s *Server) findSlingStore(rig string, agentCfg config.Agent) beads.Store { +func (s *Server) findSlingStore(rig string, agentCfg config.Agent, beadID string) beads.Store { + // Match the CLI's bead-prefix-first resolution so existence checks consult + // the bead's home store before any cross-rig guard runs. + if resolvedRig, cityScope := s.slingStoreScopeForBead(beadID); cityScope { + return s.state.CityBeadStore() + } else if resolvedRig != "" { + return s.state.BeadStore(resolvedRig) + } if rig != "" { if store := s.state.BeadStore(rig); store != nil { return store @@ -198,7 +246,12 @@ func (s *Server) findSlingStore(rig string, agentCfg config.Agent) beads.Store { } // slingStoreRef returns a store ref string for the sling context. -func (s *Server) slingStoreRef(rig string, agentCfg config.Agent) string { +func (s *Server) slingStoreRef(rig string, agentCfg config.Agent, beadID string) string { + if resolvedRig, cityScope := s.slingStoreScopeForBead(beadID); cityScope { + return "city:" + s.state.CityName() + } else if resolvedRig != "" { + return "rig:" + resolvedRig + } if rig != "" { return "rig:" + rig } @@ -208,6 +261,25 @@ func (s *Server) slingStoreRef(rig string, agentCfg config.Agent) string { return "city:" + s.state.CityName() } +func (s *Server) slingStoreScopeForBead(beadID string) (rigName string, cityScope bool) { + beadID = strings.TrimSpace(beadID) + if beadID == "" { + return "", false + } + prefix := sling.BeadPrefix(beadID) + if prefix == "" { + return "", false + } + if sling.IsHQPrefix(s.state.Config(), prefix) { + return "", true + } + rig, ok := sling.FindRigByPrefix(s.state.Config(), prefix) + if !ok { + return "", false + } + return rig.Name, false +} + func (s *Server) sourceWorkflowStores() []sling.SourceWorkflowStore { stores := make([]sling.SourceWorkflowStore, 0, len(s.state.BeadStores())+1) if cityStore := s.state.CityBeadStore(); cityStore != nil { diff --git a/internal/api/handler_sling_test.go b/internal/api/handler_sling_test.go index 7dbf7c1ca1..82949689dd 100644 --- a/internal/api/handler_sling_test.go +++ b/internal/api/handler_sling_test.go @@ -3,6 +3,7 @@ package api import ( "bytes" "encoding/json" + "errors" "io" "net/http" "net/http/httptest" @@ -10,6 +11,7 @@ import ( "path/filepath" "strings" "testing" + "time" "github.com/gastownhall/gascity/internal/agentutil" "github.com/gastownhall/gascity/internal/beads" @@ -18,6 +20,15 @@ import ( "github.com/gastownhall/gascity/internal/molecule" ) +type getErrStore struct { + beads.Store + err error +} + +func (s *getErrStore) Get(_ string) (beads.Bead, error) { + return beads.Bead{}, s.err +} + // newSlingTestServer creates a test handler wrapping a Server that has a // fake runner injected (captures commands without executing real shell // processes). @@ -83,6 +94,317 @@ func TestSlingWithBead(t *testing.T) { } } +func TestSlingWithMissingBeadReturnsBadRequest(t *testing.T) { + h, state := newSlingTestServer(t) + + body := `{"target":"myrig/worker","bead":"gc-zzzzz"}` + rec := httptest.NewRecorder() + h.ServeHTTP(rec, newPostRequest(cityURL(state, "/sling"), strings.NewReader(body))) + + if rec.Code != http.StatusBadRequest { + t.Fatalf("status = %d, want 400; body = %s", rec.Code, rec.Body.String()) + } + var problem struct { + Type string `json:"type"` + Detail string `json:"detail"` + } + if err := json.NewDecoder(rec.Body).Decode(&problem); err != nil { + t.Fatalf("decode: %v", err) + } + if problem.Type != "urn:gascity:error:sling-missing-bead" { + t.Fatalf("type = %q, want missing-bead discriminator", problem.Type) + } + if !strings.Contains(problem.Detail, "not found") { + t.Fatalf("detail = %s, want missing bead diagnostic", problem.Detail) + } + if strings.Contains(problem.Detail, "--force") { + t.Fatalf("detail = %s, want transport-neutral missing bead error", problem.Detail) + } +} + +func TestSlingAttachFormulaMissingBeadReturnsBadRequest(t *testing.T) { + h, state := newSlingTestServer(t) + + body := `{"target":"myrig/worker","formula":"code-review","attached_bead_id":"gc-zzzzz"}` + rec := httptest.NewRecorder() + h.ServeHTTP(rec, newPostRequest(cityURL(state, "/sling"), strings.NewReader(body))) + + if rec.Code != http.StatusBadRequest { + t.Fatalf("status = %d, want 400; body = %s", rec.Code, rec.Body.String()) + } + var problem struct { + Type string `json:"type"` + Detail string `json:"detail"` + } + if err := json.NewDecoder(rec.Body).Decode(&problem); err != nil { + t.Fatalf("decode: %v", err) + } + if problem.Type != "urn:gascity:error:sling-missing-bead" { + t.Fatalf("type = %q, want missing-bead discriminator", problem.Type) + } + if !strings.Contains(problem.Detail, "gc-zzzzz") || !strings.Contains(problem.Detail, "not found") { + t.Fatalf("detail = %s, want attached missing bead diagnostic", problem.Detail) + } +} + +func TestSlingWithLookupFailureReturnsInternalServerError(t *testing.T) { + h, state := newSlingTestServer(t) + state.stores["myrig"] = &getErrStore{ + Store: state.stores["myrig"], + err: errors.New("backend unavailable"), + } + var stderr bytes.Buffer + oldStderr := apiSlingStderr + apiSlingStderr = func() io.Writer { return &stderr } + t.Cleanup(func() { apiSlingStderr = oldStderr }) + + body := `{"target":"myrig/worker","bead":"gc-zzzzz"}` + rec := httptest.NewRecorder() + h.ServeHTTP(rec, newPostRequest(cityURL(state, "/sling"), strings.NewReader(body))) + + if rec.Code != http.StatusInternalServerError { + t.Fatalf("status = %d, want 500; body = %s", rec.Code, rec.Body.String()) + } + if !strings.Contains(rec.Body.String(), "sling bead lookup failed") { + t.Fatalf("body = %s, want sanitized lookup failure", rec.Body.String()) + } + if strings.Contains(rec.Body.String(), "backend unavailable") { + t.Fatalf("body = %s, should not expose backend failure detail", rec.Body.String()) + } + if !strings.Contains(stderr.String(), "backend unavailable") { + t.Fatalf("stderr = %s, want backend failure detail logged", stderr.String()) + } +} + +func TestSlingWithForceBypassesMissingBeadGuard(t *testing.T) { + h, state := newSlingTestServer(t) + + body := `{"target":"myrig/worker","bead":"gc-zzzzz","force":true}` + rec := httptest.NewRecorder() + h.ServeHTTP(rec, newPostRequest(cityURL(state, "/sling"), strings.NewReader(body))) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want 200; body = %s", rec.Code, rec.Body.String()) + } +} + +func TestSlingCrossRigExistingBeadReturnsCrossRigError(t *testing.T) { + h, state := newSlingTestServer(t) + state.stores["frontend"] = beads.NewMemStoreFrom(0, []beads.Bead{ + {ID: "FE-123", Title: "frontend task", Type: "task", Status: "open"}, + }, nil) + state.cfg.Rigs = append(state.cfg.Rigs, config.Rig{Name: "frontend", Path: "/tmp/frontend", Prefix: "FE"}) + + body := `{"rig":"myrig","target":"myrig/worker","bead":"FE-123"}` + rec := httptest.NewRecorder() + h.ServeHTTP(rec, newPostRequest(cityURL(state, "/sling"), strings.NewReader(body))) + + if rec.Code != http.StatusBadRequest { + t.Fatalf("status = %d, want 400; body = %s", rec.Code, rec.Body.String()) + } + var problem struct { + Type string `json:"type"` + Detail string `json:"detail"` + } + if err := json.NewDecoder(rec.Body).Decode(&problem); err != nil { + t.Fatalf("decode: %v", err) + } + if problem.Type != "urn:gascity:error:sling-cross-rig" { + t.Fatalf("type = %q, want cross-rig discriminator", problem.Type) + } + if !strings.Contains(problem.Detail, "cross-rig") { + t.Fatalf("detail = %s, want cross-rig diagnostic", problem.Detail) + } + if strings.Contains(problem.Detail, "not found") { + t.Fatalf("detail = %s, want cross-rig error instead of missing bead", problem.Detail) + } +} + +func TestSlingCrossRigExistingCityBeadReturnsCrossRigError(t *testing.T) { + h, state := newSlingTestServer(t) + state.cfg.Workspace.Prefix = "HQ" + state.cityBeadStore = beads.NewMemStoreFrom(0, []beads.Bead{ + {ID: "HQ-123", Title: "city task", Type: "task", Status: "open"}, + }, nil) + + body := `{"rig":"myrig","target":"myrig/worker","bead":"HQ-123"}` + rec := httptest.NewRecorder() + h.ServeHTTP(rec, newPostRequest(cityURL(state, "/sling"), strings.NewReader(body))) + + if rec.Code != http.StatusBadRequest { + t.Fatalf("status = %d, want 400; body = %s", rec.Code, rec.Body.String()) + } + var problem struct { + Type string `json:"type"` + Detail string `json:"detail"` + } + if err := json.NewDecoder(rec.Body).Decode(&problem); err != nil { + t.Fatalf("decode: %v", err) + } + if problem.Type != "urn:gascity:error:sling-cross-rig" { + t.Fatalf("type = %q, want cross-rig discriminator", problem.Type) + } + if strings.Contains(problem.Detail, "not found") { + t.Fatalf("detail = %s, want city bead to be found before cross-rig guard", problem.Detail) + } +} + +func TestSlingStoreRefReportsPrefixStoreWhenPrefixStoreMissing(t *testing.T) { + state := newFakeMutatorState(t) + state.cfg.Rigs = append(state.cfg.Rigs, config.Rig{Name: "frontend", Path: "/tmp/frontend", Prefix: "FE"}) + srv := New(state) + agentCfg := config.Agent{Name: "worker", Dir: "myrig"} + + store := srv.findSlingStore("myrig", agentCfg, "FE-123") + if store != nil { + t.Fatalf("findSlingStore returned %T, want nil missing prefix store", store) + } + if got := srv.slingStoreRef("myrig", agentCfg, "FE-123"); got != "rig:frontend" { + t.Fatalf("slingStoreRef = %q, want rig:frontend", got) + } +} + +func TestSlingPrefixStoreMissingReturnsMissingBead(t *testing.T) { + h, state := newSlingTestServer(t) + state.cfg.Rigs = append(state.cfg.Rigs, config.Rig{Name: "frontend", Path: "/tmp/frontend", Prefix: "FE"}) + + body := `{"target":"myrig/worker","bead":"FE-123"}` + rec := httptest.NewRecorder() + h.ServeHTTP(rec, newPostRequest(cityURL(state, "/sling"), strings.NewReader(body))) + + if rec.Code != http.StatusBadRequest { + t.Fatalf("status = %d, want 400; body = %s", rec.Code, rec.Body.String()) + } + var problem struct { + Type string `json:"type"` + Detail string `json:"detail"` + } + if err := json.NewDecoder(rec.Body).Decode(&problem); err != nil { + t.Fatalf("decode: %v", err) + } + if problem.Type != "urn:gascity:error:sling-missing-bead" { + t.Fatalf("type = %q, want missing-bead discriminator", problem.Type) + } + if !strings.Contains(problem.Detail, "rig:frontend") { + t.Fatalf("detail = %s, want prefix store ref", problem.Detail) + } +} + +func TestSlingForcePrefixStoreMissingFallsBackToTargetStore(t *testing.T) { + h, state := newSlingTestServer(t) + state.cfg.Rigs = append(state.cfg.Rigs, config.Rig{Name: "frontend", Path: "/tmp/frontend", Prefix: "FE"}) + + body := `{"target":"myrig/worker","bead":"FE-123","force":true}` + rec := httptest.NewRecorder() + h.ServeHTTP(rec, newPostRequest(cityURL(state, "/sling"), strings.NewReader(body))) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want 200; body = %s", rec.Code, rec.Body.String()) + } +} + +func TestSlingAttachFormulaForcePrefixStoreMissingDoesNotFallbackToTargetStore(t *testing.T) { + h, state := newSlingTestServer(t) + state.cfg.Rigs = append(state.cfg.Rigs, config.Rig{Name: "frontend", Path: "/tmp/frontend", Prefix: "FE"}) + state.stores["myrig"] = beads.NewMemStoreFrom(0, []beads.Bead{ + {ID: "FE-123", Title: "wrong-store task", Type: "task", Status: "open"}, + }, nil) + + body := `{"target":"myrig/worker","formula":"code-review","attached_bead_id":"FE-123","force":true}` + rec := httptest.NewRecorder() + h.ServeHTTP(rec, newPostRequest(cityURL(state, "/sling"), strings.NewReader(body))) + + if rec.Code != http.StatusBadRequest { + t.Fatalf("status = %d, want 400; body = %s", rec.Code, rec.Body.String()) + } + var problem struct { + Type string `json:"type"` + Detail string `json:"detail"` + } + if err := json.NewDecoder(rec.Body).Decode(&problem); err != nil { + t.Fatalf("decode: %v", err) + } + if problem.Type != "urn:gascity:error:sling-missing-bead" { + t.Fatalf("type = %q, want missing-bead discriminator; body = %s", problem.Type, rec.Body.String()) + } + if !strings.Contains(problem.Detail, "rig:frontend") { + t.Fatalf("detail = %s, want prefix store ref", problem.Detail) + } +} + +func TestSlingDefaultFormulaForcePrefixStoreMissingDoesNotFallbackToTargetStore(t *testing.T) { + h, state := newSlingTestServer(t) + state.cfg.Rigs = append(state.cfg.Rigs, config.Rig{Name: "frontend", Path: "/tmp/frontend", Prefix: "FE"}) + defaultFormula := "code-review" + state.cfg.Agents[0].DefaultSlingFormula = &defaultFormula + state.stores["myrig"] = beads.NewMemStoreFrom(0, []beads.Bead{ + {ID: "FE-123", Title: "wrong-store task", Type: "task", Status: "open"}, + }, nil) + + body := `{"target":"myrig/worker","bead":"FE-123","title":"Review this","force":true}` + rec := httptest.NewRecorder() + h.ServeHTTP(rec, newPostRequest(cityURL(state, "/sling"), strings.NewReader(body))) + + if rec.Code != http.StatusBadRequest { + t.Fatalf("status = %d, want 400; body = %s", rec.Code, rec.Body.String()) + } + var problem struct { + Type string `json:"type"` + Detail string `json:"detail"` + } + if err := json.NewDecoder(rec.Body).Decode(&problem); err != nil { + t.Fatalf("decode: %v", err) + } + if problem.Type != "urn:gascity:error:sling-missing-bead" { + t.Fatalf("type = %q, want missing-bead discriminator; body = %s", problem.Type, rec.Body.String()) + } + if !strings.Contains(problem.Detail, "rig:frontend") { + t.Fatalf("detail = %s, want prefix store ref", problem.Detail) + } +} + +func TestSlingProblemTypesDocumentedInOpenAPI(t *testing.T) { + spec := readCommittedOpenAPISpec(t) + components, err := json.Marshal(spec["components"]) + if err != nil { + t.Fatalf("marshal components: %v", err) + } + for _, want := range []string{slingMissingBeadProblemType, slingCrossRigProblemType} { + if !bytes.Contains(components, []byte(want)) { + t.Fatalf("OpenAPI components missing problem type %q", want) + } + } +} + +func TestDocumentProblemTypesIsIdempotent(t *testing.T) { + state := newFakeMutatorState(t) + sm := NewSupervisorMux(&stateCityResolver{state: state}, nil, false, "test", time.Now()) + oapi := sm.humaAPI.OpenAPI() + + documentProblemTypes(oapi) + documentProblemTypes(oapi) + + errorModel := oapi.Components.Schemas.Map()["ErrorModel"] + if errorModel == nil || errorModel.Properties == nil { + t.Fatal("ErrorModel schema missing") + } + typeSchema := errorModel.Properties["type"] + if typeSchema == nil { + t.Fatal("ErrorModel.type schema missing") + } + counts := map[string]int{} + for _, example := range typeSchema.Examples { + if s, ok := example.(string); ok { + counts[s]++ + } + } + for _, problemType := range documentedProblemTypes { + if counts[problemType] != 1 { + t.Fatalf("example count for %q = %d, want 1", problemType, counts[problemType]) + } + } +} + func TestSlingLogsMalformedCustomSlingQueryWarning(t *testing.T) { srv, state := newSlingTestServer(t) for i := range state.cfg.Agents { diff --git a/internal/api/huma_handlers_beads.go b/internal/api/huma_handlers_beads.go index 719d248bc5..f64461eb22 100644 --- a/internal/api/huma_handlers_beads.go +++ b/internal/api/huma_handlers_beads.go @@ -181,25 +181,20 @@ func (s *Server) humaHandleBeadGraph(_ context.Context, input *BeadGraphInput) ( return nil, huma.Error404NotFound("bead " + rootID + " not found") } - all, err := foundStore.List(beads.ListQuery{ - Metadata: map[string]string{"gc.root_bead_id": rootID}, - IncludeClosed: true, - }) + graphBeads, parentEdges, err := collectBeadGraph(foundStore, root) if err != nil { return nil, huma.Error500InternalServerError(err.Error()) } - - graphBeads := []beads.Bead{root} - beadIndex := map[string]beads.Bead{root.ID: root} - for _, b := range all { - if b.ID == root.ID { - continue - } - graphBeads = append(graphBeads, b) + beadIndex := make(map[string]beads.Bead, len(graphBeads)) + for _, b := range graphBeads { beadIndex[b.ID] = b } - deps, _ := collectWorkflowDeps(foundStore, beadIndex) + deps, depPartial := collectWorkflowDeps(foundStore, beadIndex) + if depPartial { + return nil, huma.Error500InternalServerError("listing bead graph dependencies failed") + } + deps = mergeWorkflowDeps(deps, parentEdges) return &IndexOutput[BeadGraphResponse]{ Index: s.latestIndex(), @@ -310,11 +305,19 @@ func (s *Server) humaHandleBeadCreate(ctx context.Context, input *BeadCreateInpu Assignee: assignee, Description: input.Body.Description, Labels: input.Body.Labels, + ParentID: input.Body.Parent, + Metadata: input.Body.Metadata, }) if err != nil { s.idem.unreserve(idemKey) return nil, huma.Error500InternalServerError(err.Error()) } + + // Some stores return a minimal create envelope and require a follow-up + // read for the canonical persisted bead state. + if persisted, getErr := store.Get(b.ID); getErr == nil { + b = persisted + } s.idem.storeResponse(idemKey, bodyHash, b) return &IndexOutput[beads.Bead]{ @@ -421,6 +424,14 @@ func (s *Server) humaHandleBeadUpdate(ctx context.Context, input *BeadUpdateInpu Description: body.Description, Labels: body.Labels, RemoveLabels: body.RemoveLabels, + Metadata: body.Metadata, + } + if body.parentSet { + parent := "" + if body.Parent != nil { + parent = *body.Parent + } + opts.ParentID = &parent } for _, store := range s.beadStoresForID(id) { @@ -447,12 +458,6 @@ func (s *Server) humaHandleBeadUpdate(ctx context.Context, input *BeadUpdateInpu } return nil, huma.Error500InternalServerError(err.Error()) } - // Apply metadata key-value pairs if provided. - if len(body.Metadata) > 0 { - if err := store.SetMetadataBatch(id, body.Metadata); err != nil { - return nil, huma.Error500InternalServerError(err.Error()) - } - } resp := &OKResponse{} resp.Body.Status = "updated" return resp, nil diff --git a/internal/api/huma_handlers_config.go b/internal/api/huma_handlers_config.go index dbec8d8ed5..19e951a0e3 100644 --- a/internal/api/huma_handlers_config.go +++ b/internal/api/huma_handlers_config.go @@ -44,7 +44,9 @@ func (s *Server) humaHandleConfigGet(_ context.Context, _ *ConfigGetInput) (*Ind providers[name] = providerSpecJSON{ DisplayName: spec.DisplayName, Command: spec.Command, + ACPCommand: spec.ACPCommand, Args: spec.Args, + ACPArgs: optionalStringSlice(spec.ACPArgs), PromptMode: spec.PromptMode, PromptFlag: spec.PromptFlag, ReadyDelayMs: spec.ReadyDelayMs, @@ -129,7 +131,9 @@ func (s *Server) humaHandleConfigExplain(_ context.Context, _ *ConfigExplainInpu provMap[name] = annotatedProviderResponse{ DisplayName: spec.DisplayName, Command: spec.Command, + ACPCommand: spec.ACPCommand, Args: spec.Args, + ACPArgs: optionalStringSlice(spec.ACPArgs), PromptMode: spec.PromptMode, PromptFlag: spec.PromptFlag, ReadyDelayMs: spec.ReadyDelayMs, @@ -143,7 +147,9 @@ func (s *Server) humaHandleConfigExplain(_ context.Context, _ *ConfigExplainInpu provMap[name] = annotatedProviderResponse{ DisplayName: spec.DisplayName, Command: spec.Command, + ACPCommand: spec.ACPCommand, Args: spec.Args, + ACPArgs: optionalStringSlice(spec.ACPArgs), PromptMode: spec.PromptMode, PromptFlag: spec.PromptFlag, ReadyDelayMs: spec.ReadyDelayMs, @@ -220,7 +226,9 @@ type annotatedAgentResponse struct { type annotatedProviderResponse struct { DisplayName string `json:"display_name,omitempty"` Command string `json:"command,omitempty"` + ACPCommand string `json:"acp_command,omitempty"` Args []string `json:"args,omitempty"` + ACPArgs *[]string `json:"acp_args,omitempty"` PromptMode string `json:"prompt_mode,omitempty"` PromptFlag string `json:"prompt_flag,omitempty"` ReadyDelayMs int `json:"ready_delay_ms,omitempty"` diff --git a/internal/api/huma_handlers_patches.go b/internal/api/huma_handlers_patches.go index ec215ab8ad..8ffb58248c 100644 --- a/internal/api/huma_handlers_patches.go +++ b/internal/api/huma_handlers_patches.go @@ -222,7 +222,9 @@ func (s *Server) humaHandleProviderPatchSet(_ context.Context, input *ProviderPa patch := config.ProviderPatch{ Name: input.Body.Name, Command: input.Body.Command, + ACPCommand: input.Body.ACPCommand, Args: input.Body.Args, + ACPArgs: input.Body.ACPArgs, PromptMode: input.Body.PromptMode, PromptFlag: input.Body.PromptFlag, ReadyDelayMs: input.Body.ReadyDelayMs, diff --git a/internal/api/huma_handlers_providers.go b/internal/api/huma_handlers_providers.go index d5a9a82e38..9fd3132b3a 100644 --- a/internal/api/huma_handlers_providers.go +++ b/internal/api/huma_handlers_providers.go @@ -140,7 +140,9 @@ func (s *Server) humaHandleProviderCreate(_ context.Context, input *ProviderCrea DisplayName: input.Body.DisplayName, Base: input.Body.Base, Command: input.Body.Command, + ACPCommand: input.Body.ACPCommand, Args: input.Body.Args, + ACPArgs: input.Body.ACPArgs, ArgsAppend: input.Body.ArgsAppend, PromptMode: input.Body.PromptMode, PromptFlag: input.Body.PromptFlag, @@ -172,7 +174,9 @@ func (s *Server) humaHandleProviderUpdate(_ context.Context, input *ProviderUpda patch := ProviderUpdate{ DisplayName: input.Body.DisplayName, Command: input.Body.Command, + ACPCommand: input.Body.ACPCommand, Args: input.Body.Args, + ACPArgs: input.Body.ACPArgs, ArgsAppend: input.Body.ArgsAppend, PromptMode: input.Body.PromptMode, PromptFlag: input.Body.PromptFlag, diff --git a/internal/api/huma_handlers_sessions_command.go b/internal/api/huma_handlers_sessions_command.go index 6a1b16e054..fb2ba5ec41 100644 --- a/internal/api/huma_handlers_sessions_command.go +++ b/internal/api/huma_handlers_sessions_command.go @@ -17,7 +17,7 @@ import ( "github.com/gastownhall/gascity/internal/runtime" "github.com/gastownhall/gascity/internal/session" "github.com/gastownhall/gascity/internal/sessionlog" - workdirutil "github.com/gastownhall/gascity/internal/workdir" + "github.com/gastownhall/gascity/internal/worker" ) // Command-side session handlers (create, patch, submit, message, stop, kill, @@ -56,6 +56,10 @@ func (s *Server) humaHandleSessionCreate(ctx context.Context, input *SessionCrea } return nil, huma.Error500InternalServerError(err.Error()) } + transport, err = validateSessionTransport(resolved, transport, s.state.SessionProvider()) + if err != nil { + return nil, huma.Error503ServiceUnavailable(err.Error()) + } if len(body.Options) > 0 { if len(resolved.OptionsSchema) == 0 { @@ -74,12 +78,6 @@ func (s *Server) humaHandleSessionCreate(ctx context.Context, input *SessionCrea title = template } - resume := session.ProviderResume{ - ResumeFlag: resolved.ResumeFlag, - ResumeStyle: resolved.ResumeStyle, - ResumeCommand: resolved.ResumeCommand, - SessionIDFlag: resolved.SessionIDFlag, - } alias, err := session.ValidateAlias(body.Alias) if err != nil { return nil, humaSessionManagerError(err) @@ -88,27 +86,27 @@ func (s *Server) humaHandleSessionCreate(ctx context.Context, input *SessionCrea if cfg == nil { return nil, huma.Error500InternalServerError("no city config loaded") } - agentCfg, ok := resolveSessionTemplateAgent(cfg, template) - if !ok { - return nil, huma.Error500InternalServerError("resolved agent template disappeared: " + template) - } - if alias != "" && agentCfg.SupportsMultipleSessions() { - alias = workdirutil.SessionQualifiedName(s.state.CityPath(), agentCfg, cfg.Rigs, alias, "") - } - explicitName, err := sessionExplicitNameForCreate(agentCfg, alias) + createCtx, err := s.resolveAgentCreateContext(template, alias) if err != nil { - return nil, humaSessionManagerError(err) + return nil, huma.Error500InternalServerError(err.Error()) } - workDirQualifiedName := workdirutil.SessionQualifiedName(s.state.CityPath(), agentCfg, cfg.Rigs, alias, explicitName) - workDir, err = s.resolveSessionWorkDir(agentCfg, workDirQualifiedName) + agentCfg := createCtx.Agent + alias = createCtx.Alias + explicitName := createCtx.ExplicitName + workDirQualifiedName := createCtx.Identity + workDir = createCtx.WorkDir + + launchCommand, err := config.BuildProviderLaunchCommandWithoutOptions(s.state.CityPath(), resolved, transport) if err != nil { return nil, huma.Error500InternalServerError(err.Error()) } - - command := sessionCreateAgentCommand(resolved) + command := launchCommand.Command extraMeta := sessionTemplateOverridesMetadata(body.Options, body.Message) + mcpServers, err := s.sessionMCPServers(template, resolved.Name, workDirQualifiedName, workDir, transport, kind) + if err != nil { + return nil, huma.Error500InternalServerError(err.Error()) + } - mgr := s.sessionManager(store) var info session.Info reservationIDs := []string{alias, explicitName} reserveConcreteIdentity := agentCfg.SupportsMultipleSessions() && strings.TrimSpace(workDirQualifiedName) != "" @@ -132,19 +130,34 @@ func (s *Server) humaHandleSessionCreate(ctx context.Context, input *SessionCrea } extraMeta["agent_name"] = workDirQualifiedName extraMeta["session_origin"] = "manual" - var createErr error - info, createErr = mgr.CreateAliasedBeadOnlyNamedWithMetadata( + if transport == "acp" { + var mcpMetaErr error + extraMeta, mcpMetaErr = session.WithStoredMCPMetadata(extraMeta, workDirQualifiedName, mcpServers) + if mcpMetaErr != nil { + return mcpMetaErr + } + } + resolvedCfg, cfgErr := resolvedSessionConfigForProvider( alias, explicitName, template, title, - command, - workDir, - resolved.Name, transport, - resume, extraMeta, + resolved, + command, + workDir, + mcpServers, ) + if cfgErr != nil { + return cfgErr + } + handle, handleErr := s.newResolvedWorkerSessionHandle(store, resolvedCfg) + if handleErr != nil { + return handleErr + } + var createErr error + info, createErr = handle.Create(ctx, worker.CreateModeDeferred) return createErr }) if err != nil { @@ -233,22 +246,43 @@ func (s *Server) humaCreateProviderSession(ctx context.Context, store beads.Stor if err != nil { return nil, humaSessionManagerError(err) } + mcpIdentity, err := providerSessionMCPIdentity(resolved.Name, alias) + if err != nil { + return nil, huma.Error500InternalServerError(err.Error()) + } - launchCommand, err := config.BuildProviderLaunchCommand(s.state.CityPath(), resolved, body.Options) + transport, err := providerSessionTransport(resolved, s.state.SessionProvider()) + if err != nil { + return nil, huma.Error503ServiceUnavailable(err.Error()) + } + launchCommand, err := config.BuildProviderLaunchCommand(s.state.CityPath(), resolved, body.Options, transport) if err != nil { return nil, huma.Error400BadRequest(err.Error()) } command := launchCommand.Command + mcpServers, err := s.providerSessionMCPServers(resolved.Name, mcpIdentity, workDir, transport) + if err != nil { + return nil, huma.Error500InternalServerError(err.Error()) + } + extraMeta := map[string]string{ + "session_origin": "manual", + } + if transport == "acp" { + extraMeta, err = session.WithStoredMCPMetadata(extraMeta, mcpIdentity, mcpServers) + if err != nil { + return nil, huma.Error500InternalServerError(err.Error()) + } + } mgr := s.sessionManager(store) - hints := sessionCreateHints(resolved) + hints := sessionCreateHints(resolved, mcpServers) var info session.Info err = session.WithCitySessionAliasLock(s.state.CityPath(), alias, func() error { if aliasErr := session.EnsureAliasAvailableWithConfig(store, s.state.Config(), alias, ""); aliasErr != nil { return aliasErr } var createErr error - info, createErr = mgr.CreateAliasedNamedWithTransport( + info, createErr = mgr.CreateAliasedNamedWithTransportAndMetadata( ctx, alias, "", @@ -257,10 +291,11 @@ func (s *Server) humaCreateProviderSession(ctx context.Context, store beads.Stor command, workDir, resolved.Name, - "", + transport, resolved.Env, resume, hints, + extraMeta, ) return createErr }) diff --git a/internal/api/huma_handlers_sling.go b/internal/api/huma_handlers_sling.go index f996c5ff76..34a48017ee 100644 --- a/internal/api/huma_handlers_sling.go +++ b/internal/api/huma_handlers_sling.go @@ -28,6 +28,7 @@ func (s *Server) humaHandleSling(ctx context.Context, input *SlingInput) (*Sling Vars: input.Body.Vars, ScopeKind: input.Body.ScopeKind, ScopeRef: input.Body.ScopeRef, + Force: input.Body.Force, } if body.Target == "" { @@ -100,7 +101,7 @@ func (s *Server) humaHandleSling(ctx context.Context, input *SlingInput) (*Sling // only a string detail, so we build the Problem Details error // manually with structured extensions. if conflict != nil && status == http.StatusConflict { - storeRef := s.slingStoreRef(body.Rig, agentCfg) + storeRef := s.slingStoreRef(body.Rig, agentCfg, slingStoreBeadID(body)) hint := sourceWorkflowCleanupHint(conflict.SourceBeadID, storeRef) return nil, &huma.ErrorModel{ Status: http.StatusConflict, @@ -113,6 +114,25 @@ func (s *Server) humaHandleSling(ctx context.Context, input *SlingInput) (*Sling }, } } + if status >= http.StatusInternalServerError { + return nil, huma.Error500InternalServerError(message) + } + if code == "missing_bead" { + return nil, &huma.ErrorModel{ + Type: slingMissingBeadProblemType, + Status: http.StatusBadRequest, + Title: http.StatusText(http.StatusBadRequest), + Detail: message, + } + } + if code == "cross_rig" { + return nil, &huma.ErrorModel{ + Type: slingCrossRigProblemType, + Status: http.StatusBadRequest, + Title: http.StatusText(http.StatusBadRequest), + Detail: message, + } + } return nil, huma.Error400BadRequest(message) } diff --git a/internal/api/huma_handlers_supervisor.go b/internal/api/huma_handlers_supervisor.go index 0c7c92fbea..dbb6a8c4bf 100644 --- a/internal/api/huma_handlers_supervisor.go +++ b/internal/api/huma_handlers_supervisor.go @@ -1,14 +1,12 @@ package api import ( - "bytes" "context" "errors" "fmt" "log" "net/http" "os" - "os/exec" "path/filepath" "sort" "strings" @@ -16,8 +14,8 @@ import ( "github.com/danielgtaylor/huma/v2" "github.com/danielgtaylor/huma/v2/adapters/humago" + "github.com/gastownhall/gascity/internal/cityinit" "github.com/gastownhall/gascity/internal/citylayout" - "github.com/gastownhall/gascity/internal/config" "github.com/gastownhall/gascity/internal/events" ) @@ -79,10 +77,21 @@ type cityCreateRequest struct { BootstrapProfile string `json:"bootstrap_profile,omitempty" enum:"k8s-cell,kubernetes,kubernetes-cell,single-host-compat" doc:"Optional bootstrap profile."` } -// cityCreateResponse is the response body for POST /v0/city. +// cityCreateResponse is the response body for POST /v0/city. This +// endpoint is asynchronous: a 202 response means the city was +// scaffolded on disk and registered with the supervisor, but the +// supervisor reconciler still has to run the slow finalize work +// (pack materialization, bead store startup, formula resolution, +// agent validation). Clients observe completion by subscribing to +// /v0/events/stream and waiting for a city.ready event (payload +// CityReadyPayload.Name == this response's Name) or a +// city.init_failed event (CityInitFailedPayload.Name == this +// response's Name; Error field explains the failure). Polling is +// unnecessary. type cityCreateResponse struct { - OK bool `json:"ok" doc:"True on success."` - Path string `json:"path" doc:"Resolved absolute path of the created city."` + OK bool `json:"ok" doc:"True when scaffolding + registration succeeded. Does not imply the city is ready yet; watch /v0/events/stream for city.ready."` + Name string `json:"name" doc:"Resolved city name as persisted in city.toml. Use this to filter the event stream for completion."` + Path string `json:"path" doc:"Resolved absolute path of the created city directory."` } // SupervisorCityCreateInput is the input for POST /v0/city. @@ -90,9 +99,41 @@ type SupervisorCityCreateInput struct { Body cityCreateRequest } -// SupervisorCityCreateOutput is the response for POST /v0/city. +// SupervisorCityCreateOutput is the response for POST /v0/city. The +// Status field carries 202 Accepted to tell Huma to emit the async +// status code; see humaHandleCityCreate for the rationale and event +// contract. type SupervisorCityCreateOutput struct { - Body cityCreateResponse + Status int `json:"-"` + Body cityCreateResponse +} + +// cityUnregisterResponse is the response body for +// POST /v0/city/{cityName}/unregister. This endpoint is asynchronous: +// a 202 response means the city's registry entry was removed and the +// supervisor was signaled to reconcile, but the city's controller is +// not yet stopped. Clients observe completion by subscribing to +// /v0/events/stream and waiting for a city.unregistered event (or +// city.unregister_failed if the reconciler cannot stop the +// controller). +type cityUnregisterResponse struct { + OK bool `json:"ok" doc:"True when the registry entry was removed and the supervisor was signaled. Does not imply the city's controller has stopped yet; watch /v0/events/stream for city.unregistered."` + Name string `json:"name" doc:"Resolved registry name. Filter the event stream by this to observe completion."` + Path string `json:"path" doc:"Resolved absolute city directory. The directory itself is not modified; unregister only affects the supervisor's registry."` +} + +// SupervisorCityUnregisterInput is the input for +// POST /v0/city/{cityName}/unregister. +type SupervisorCityUnregisterInput struct { + CityName string `path:"cityName" doc:"Supervisor-registered city name."` +} + +// SupervisorCityUnregisterOutput is the response for +// POST /v0/city/{cityName}/unregister. The Status field carries +// 202 Accepted to tell Huma to emit the async status code. +type SupervisorCityUnregisterOutput struct { + Status int `json:"-"` + Body cityUnregisterResponse } // SupervisorEventListInput is the input for GET /v0/events (supervisor scope). @@ -117,12 +158,6 @@ type SupervisorEventStreamInput struct { AfterCursor string `query:"after_cursor" required:"false" doc:"Alternative to Last-Event-ID for browsers that can't set custom headers."` } -// cityInitExitAlreadyInitialized mirrors initExitAlreadyInitialized in -// cmd/gc/cmd_init.go. Duplicated here because the CLI's exit-code -// constant is declared in the main package and not importable; the two -// must stay in sync. -const cityInitExitAlreadyInitialized = 2 - // --- Huma API setup --- // newSupervisorHumaAPI builds a huma.API attached to mux for supervisor- @@ -139,6 +174,7 @@ func newSupervisorHumaAPI(mux *http.ServeMux, readOnly bool) huma.API { // Force-register documentation-only union schemas so they appear in // components.schemas even though no handler names them directly. _ = SessionStreamCommonEvent{}.Schema(api.OpenAPI().Components.Schemas) + registerEventEnvelopeCompatibilitySchemas(api.OpenAPI().Components.Schemas) api.UseMiddleware(humaCSRFMiddleware(api)) if readOnly { @@ -178,7 +214,18 @@ func (sm *SupervisorMux) registerSupervisorRoutes() { huma.Get(sm.humaAPI, "/health", sm.humaHandleHealth) huma.Get(sm.humaAPI, "/v0/readiness", sm.humaHandleReadiness) huma.Get(sm.humaAPI, "/v0/provider-readiness", sm.humaHandleProviderReadiness) - huma.Post(sm.humaAPI, "/v0/city", sm.humaHandleCityCreate) + // Async mutation: returns 202 Accepted after scaffold + register; + // completion signaled via city.ready / city.init_failed events. + huma.Post(sm.humaAPI, "/v0/city", sm.humaHandleCityCreate, addMutationCSRFParam, func(op *huma.Operation) { + op.DefaultStatus = http.StatusAccepted + }) + // Async unregister: returns 202 after the registry entry is + // removed and the supervisor is signaled. city.unregistered / + // city.unregister_failed events signal completion on the event + // stream. + huma.Post(sm.humaAPI, "/v0/city/{cityName}/unregister", sm.humaHandleCityUnregister, addMutationCSRFParam, func(op *huma.Operation) { + op.DefaultStatus = http.StatusAccepted + }) huma.Get(sm.humaAPI, "/v0/events", sm.humaHandleEventList) registerSSEStringID(sm.humaAPI, huma.Operation{ @@ -187,8 +234,11 @@ func (sm *SupervisorMux) registerSupervisorRoutes() { Path: "/v0/events/stream", Summary: "Stream tagged events from all running cities.", }, map[string]any{ - "tagged_event": &taggedEventStreamEnvelope{}, - "heartbeat": HeartbeatEvent{}, + "tagged_event": sseEventContract{ + runtimeSample: &taggedEventStreamEnvelope{}, + schemaSample: typedTaggedEventStreamEnvelopeSchema{}, + }, + "heartbeat": HeartbeatEvent{}, }, sm.precheckGlobalEventStream, sm.streamGlobalEvents) } @@ -276,17 +326,24 @@ func (sm *SupervisorMux) humaHandleProviderReadiness(ctx context.Context, input return out, nil } -// humaHandleCityCreate handles POST /v0/city — create a new city by -// shelling out to `gc init`. Stateless; does not require a running city. +// humaHandleCityCreate handles POST /v0/city asynchronously. Calls +// Initializer.Scaffold in-process to write the on-disk shape and +// register the city with the supervisor, then returns 202 Accepted +// immediately. The supervisor reconciler runs the slow finalize +// (prepareCityForSupervisor: pack materialization, bead store +// startup, formula resolution, agent validation) on its next tick +// and emits city.ready / city.init_failed events on the supervisor +// event bus when done. Clients observe completion via +// /v0/events/stream — no polling required. +// +// Rationale: full city init takes minutes (dolt startup, +// provider-readiness probes, pack fetch). Blocking the HTTP request +// until finalize completes exceeds reasonable client timeouts +// (MC's harness hit 120s). The fast scaffold+register path takes +// seconds; the async completion contract via SSE is the right shape +// for a long-running operation. See engdocs/architecture/api-control-plane.md §1–§2 on +// the object model + typed events; §4 on the event registry. func (sm *SupervisorMux) humaHandleCityCreate(ctx context.Context, input *SupervisorCityCreateInput) (*SupervisorCityCreateOutput, error) { - // Dir/Provider emptiness is enforced by minLength:"1" tags on cityCreateRequest. - // BootstrapProfile membership is enforced by the enum tag. - // Provider membership against runtime-loaded builtins stays here — - // static enum can't express a runtime-loaded set. - if _, ok := config.BuiltinProviders()[input.Body.Provider]; !ok { - return nil, huma.Error422UnprocessableEntity(fmt.Sprintf("invalid: unknown provider %q", input.Body.Provider)) - } - dir := input.Body.Dir if !filepath.IsAbs(dir) { home, err := os.UserHomeDir() @@ -296,46 +353,87 @@ func (sm *SupervisorMux) humaHandleCityCreate(ctx context.Context, input *Superv dir = filepath.Join(home, dir) } - if err := os.MkdirAll(dir, 0o755); err != nil { - return nil, huma.Error500InternalServerError(fmt.Sprintf("internal: creating directory: %v", err)) - } + // Cheap pre-check that does not require an Initializer: if the + // target directory already looks like an initialized city on disk, + // return 409 before we try to scaffold. Keeps the API well-behaved + // in test configurations that build a SupervisorMux without an + // Initializer. if cityDirAlreadyInitialized(dir) { return nil, huma.Error409Conflict("conflict: city already initialized at " + dir) } - gcBin, err := os.Executable() - if err != nil { - return nil, huma.Error500InternalServerError(fmt.Sprintf("internal: finding gc binary: %v", err)) + if sm.initializer == nil { + return nil, huma.Error501NotImplemented("city creation is not available in this supervisor (no initializer wired)") } - args := []string{"init", dir, "--provider", input.Body.Provider} - if input.Body.BootstrapProfile != "" { - args = append(args, "--bootstrap-profile", input.Body.BootstrapProfile) + + result, err := sm.initializer.Scaffold(ctx, cityinit.InitRequest{ + Dir: dir, + Provider: input.Body.Provider, + BootstrapProfile: input.Body.BootstrapProfile, + // The async API defers dependency/provider blockers to the + // reconciler's terminal city.init_failed event instead of + // failing POST synchronously. + SkipProviderReadiness: true, + }) + switch { + case errors.Is(err, cityinit.ErrAlreadyInitialized): + return nil, huma.Error409Conflict("conflict: city already initialized at " + dir) + case errors.Is(err, cityinit.ErrInvalidProvider), + errors.Is(err, cityinit.ErrInvalidBootstrapProfile): + return nil, huma.Error422UnprocessableEntity(err.Error()) + case err != nil: + return nil, huma.Error500InternalServerError(err.Error()) } - cctx, cancel := context.WithTimeout(ctx, 30*time.Second) - defer cancel() + out := &SupervisorCityCreateOutput{ + Status: http.StatusAccepted, + } + out.Body = cityCreateResponse{ + OK: true, + Name: result.CityName, + Path: result.CityPath, + } + return out, nil +} - cmd := exec.CommandContext(cctx, gcBin, args...) - var stderr bytes.Buffer - cmd.Stderr = &stderr +// humaHandleCityUnregister handles POST /v0/city/{cityName}/unregister +// asynchronously. Calls Initializer.Unregister in-process to remove +// the city from the supervisor's registry and signal reconcile, then +// returns 202 Accepted immediately. The supervisor reconciler stops +// the city's controller on its next tick and emits city.unregistered +// (or city.unregister_failed on stop failure) on the supervisor +// event bus. Clients observe completion via /v0/events/stream — no +// polling required. +// +// The city directory itself is not modified. Purging the directory +// is a separate concern. +// +// Error mapping: +// - ErrNotRegistered -> 404 Not Found +// - any other error -> 500 Internal Server Error +func (sm *SupervisorMux) humaHandleCityUnregister(ctx context.Context, input *SupervisorCityUnregisterInput) (*SupervisorCityUnregisterOutput, error) { + if sm.initializer == nil { + return nil, huma.Error501NotImplemented("city unregister is not available in this supervisor (no initializer wired)") + } + name := strings.TrimSpace(input.CityName) + if name == "" { + return nil, huma.Error400BadRequest("city_name is required") + } - if err := cmd.Run(); err != nil { - msg := stderr.String() - if msg == "" { - msg = err.Error() - } - // gc init exits with initExitAlreadyInitialized when the - // target already contains a city. Dispatch on the exit code - // rather than scraping stderr text. - var exitErr *exec.ExitError - if errors.As(err, &exitErr) && exitErr.ExitCode() == cityInitExitAlreadyInitialized { - return nil, huma.Error409Conflict("conflict: city already initialized at " + dir) - } - return nil, huma.Error500InternalServerError("init_failed: " + msg) + result, err := sm.initializer.Unregister(ctx, cityinit.UnregisterRequest{CityName: name}) + switch { + case errors.Is(err, cityinit.ErrNotRegistered): + return nil, huma.Error404NotFound("not_found: " + err.Error()) + case err != nil: + return nil, huma.Error500InternalServerError(err.Error()) } - out := &SupervisorCityCreateOutput{} - out.Body = cityCreateResponse{OK: true, Path: dir} + out := &SupervisorCityUnregisterOutput{Status: http.StatusAccepted} + out.Body = cityUnregisterResponse{ + OK: true, + Name: result.CityName, + Path: result.CityPath, + } return out, nil } @@ -380,10 +478,9 @@ func (sm *SupervisorMux) humaHandleEventList(_ context.Context, input *Superviso // Total is the full match count so clients can distinguish "limit // truncated" from "the server only had N events." out.Body.Total = len(wires) - // Limit clamp: when a caller asks for limit=N, return the N most - // recent events (the list is already chronologically ordered, so we - // take the tail). Critical for `gc events --seq` which computes the - // head cursor from the last event only. + // Limit clamp: take the N most recent events (wires is already + // chronologically ordered). Critical for `gc events --seq` which + // computes the head cursor from the last event only. if input.Limit > 0 && input.Limit < len(wires) { wires = wires[len(wires)-input.Limit:] } @@ -393,11 +490,16 @@ func (sm *SupervisorMux) humaHandleEventList(_ context.Context, input *Superviso // --- Supervisor global events stream (Fix 3g final wiring) --- -// precheckGlobalEventStream validates that the global event stream can -// actually deliver events before committing 200 headers. Two failure -// modes both produce 503 Problem Details instead of 200+EOF: +// precheckGlobalEventStream validates that the global event stream +// can actually deliver events before committing 200 headers. Two +// failure modes both produce 503 Problem Details instead of 200+EOF: // -// 1. No event providers registered at all (empty mux). +// 1. No event providers registered at all (empty mux). In practice +// this only happens when zero cities are registered in the +// supervisor — the TransientCityEventSource resolver extension +// surfaces event files for every registered city (running, +// pending, or failed) so any POST /v0/city → subscribe flow +// finds the newly-registered city in the mux. // 2. Providers exist but none can attach a watcher right now. // // The precheck attaches a watcher and closes it immediately — a diff --git a/internal/api/huma_handlers_supervisor_test.go b/internal/api/huma_handlers_supervisor_test.go index b97fed7097..f604464964 100644 --- a/internal/api/huma_handlers_supervisor_test.go +++ b/internal/api/huma_handlers_supervisor_test.go @@ -1,16 +1,55 @@ package api import ( + "context" + "errors" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" + "time" + "github.com/gastownhall/gascity/internal/cityinit" "github.com/gastownhall/gascity/internal/citylayout" ) +type fakeInitializer struct { + scaffoldReq cityinit.InitRequest + scaffoldResult *cityinit.InitResult + scaffoldErr error + + unregisterReq cityinit.UnregisterRequest + unregisterResult *cityinit.UnregisterResult + unregisterErr error +} + +func (f *fakeInitializer) Init(context.Context, cityinit.InitRequest) (*cityinit.InitResult, error) { + return nil, errors.New("Init should not be called by supervisor tests") +} + +func (f *fakeInitializer) Scaffold(_ context.Context, req cityinit.InitRequest) (*cityinit.InitResult, error) { + f.scaffoldReq = req + if f.scaffoldErr != nil { + return nil, f.scaffoldErr + } + return f.scaffoldResult, nil +} + +func (f *fakeInitializer) Unregister(_ context.Context, req cityinit.UnregisterRequest) (*cityinit.UnregisterResult, error) { + f.unregisterReq = req + if f.unregisterErr != nil { + return nil, f.unregisterErr + } + return f.unregisterResult, nil +} + +func newTestSupervisorMuxWithInitializer(t *testing.T, init cityinit.Initializer) *SupervisorMux { + t.Helper() + return NewSupervisorMux(&fakeCityResolver{cities: map[string]*fakeState{}}, init, false, "test", time.Now()) +} + func TestSupervisorCityCreateConflictsWhenTargetAlreadyInitialized(t *testing.T) { tests := []struct { name string @@ -60,6 +99,132 @@ func TestSupervisorCityCreateConflictsWhenTargetAlreadyInitialized(t *testing.T) } } +func TestSupervisorCityCreateScaffoldsViaInitializer(t *testing.T) { + home := t.TempDir() + t.Setenv("HOME", home) + cityPath := filepath.Join(home, "mc-city") + init := &fakeInitializer{ + scaffoldResult: &cityinit.InitResult{ + CityName: "mc-city", + CityPath: cityPath, + ProviderUsed: "codex", + }, + } + sm := newTestSupervisorMuxWithInitializer(t, init) + + req := httptest.NewRequest(http.MethodPost, "/v0/city", strings.NewReader(`{ + "dir":"mc-city", + "provider":"codex", + "bootstrap_profile":"single-host-compat" + }`)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-GC-Request", "test") + rec := httptest.NewRecorder() + + sm.ServeHTTP(rec, req) + + if rec.Code != http.StatusAccepted { + t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusAccepted, rec.Body.String()) + } + if init.scaffoldReq.Dir != cityPath { + t.Fatalf("Scaffold Dir = %q, want %q", init.scaffoldReq.Dir, cityPath) + } + if init.scaffoldReq.Provider != "codex" || init.scaffoldReq.BootstrapProfile != "single-host-compat" { + t.Fatalf("Scaffold request = %+v, want codex + single-host-compat", init.scaffoldReq) + } + if !init.scaffoldReq.SkipProviderReadiness { + t.Fatal("Scaffold request should skip provider readiness for API callers") + } + if body := rec.Body.String(); !strings.Contains(body, `"name":"mc-city"`) || !strings.Contains(body, `"path":"`+cityPath+`"`) { + t.Fatalf("body = %s, want name and path", body) + } +} + +func TestSupervisorCityCreateMapsInitializerErrors(t *testing.T) { + cityPath := filepath.Join(t.TempDir(), "mc-city") + tests := []struct { + name string + err error + want int + }{ + {name: "already initialized", err: cityinit.ErrAlreadyInitialized, want: http.StatusConflict}, + {name: "invalid provider", err: cityinit.ErrInvalidProvider, want: http.StatusUnprocessableEntity}, + {name: "invalid bootstrap", err: cityinit.ErrInvalidBootstrapProfile, want: http.StatusUnprocessableEntity}, + {name: "generic", err: errors.New("boom"), want: http.StatusInternalServerError}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + init := &fakeInitializer{scaffoldErr: tc.err} + sm := newTestSupervisorMuxWithInitializer(t, init) + req := httptest.NewRequest(http.MethodPost, "/v0/city", strings.NewReader(`{"dir":"`+cityPath+`","provider":"codex"}`)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-GC-Request", "test") + rec := httptest.NewRecorder() + + sm.ServeHTTP(rec, req) + + if rec.Code != tc.want { + t.Fatalf("status = %d, want %d; body=%s", rec.Code, tc.want, rec.Body.String()) + } + }) + } +} + +func TestSupervisorCityCreateWithoutInitializerReturns501(t *testing.T) { + sm := newTestSupervisorMux(t, map[string]*fakeState{}) + cityPath := filepath.Join(t.TempDir(), "mc-city") + req := httptest.NewRequest(http.MethodPost, "/v0/city", strings.NewReader(`{"dir":"`+cityPath+`","provider":"codex"}`)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-GC-Request", "test") + rec := httptest.NewRecorder() + + sm.ServeHTTP(rec, req) + + if rec.Code != http.StatusNotImplemented { + t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusNotImplemented, rec.Body.String()) + } +} + +func TestSupervisorCityUnregisterUsesInitializer(t *testing.T) { + init := &fakeInitializer{ + unregisterResult: &cityinit.UnregisterResult{ + CityName: "mc-city", + CityPath: "/tmp/mc-city", + }, + } + sm := newTestSupervisorMuxWithInitializer(t, init) + req := httptest.NewRequest(http.MethodPost, "/v0/city/mc-city/unregister", nil) + req.Header.Set("X-GC-Request", "test") + rec := httptest.NewRecorder() + + sm.ServeHTTP(rec, req) + + if rec.Code != http.StatusAccepted { + t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusAccepted, rec.Body.String()) + } + if init.unregisterReq.CityName != "mc-city" { + t.Fatalf("Unregister CityName = %q, want mc-city", init.unregisterReq.CityName) + } + if body := rec.Body.String(); !strings.Contains(body, `"name":"mc-city"`) || !strings.Contains(body, `"path":"/tmp/mc-city"`) { + t.Fatalf("body = %s, want name and path", body) + } +} + +func TestSupervisorCityUnregisterMapsNotRegistered(t *testing.T) { + init := &fakeInitializer{unregisterErr: cityinit.ErrNotRegistered} + sm := newTestSupervisorMuxWithInitializer(t, init) + req := httptest.NewRequest(http.MethodPost, "/v0/city/missing/unregister", nil) + req.Header.Set("X-GC-Request", "test") + rec := httptest.NewRecorder() + + sm.ServeHTTP(rec, req) + + if rec.Code != http.StatusNotFound { + t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusNotFound, rec.Body.String()) + } +} + func TestCityDirAlreadyInitializedAllowsConfigOnlyBootstrap(t *testing.T) { dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, citylayout.CityConfigFile), []byte("[workspace]\nname = \"alpha\"\n"), 0o644); err != nil { diff --git a/internal/api/huma_optional_param.go b/internal/api/huma_optional_param.go new file mode 100644 index 0000000000..0bad36a341 --- /dev/null +++ b/internal/api/huma_optional_param.go @@ -0,0 +1,58 @@ +package api + +// OptionalParam wraps a query-parameter value with a presence flag so +// handlers can distinguish "parameter absent" from "parameter present +// with zero value" without reading raw URL values. +// +// This is the Huma-documented idiom for presence detection on query +// parameters. See https://huma.rocks/features/request-inputs/ and the +// sample in Huma's own huma_test.go. Huma v2 does not support pointer +// query parameters (see github.com/danielgtaylor/huma issue #288), +// which is why this wrapper exists instead of *T. +// +// Usage: +// +// type Input struct { +// Cursor OptionalParam[string] `query:"cursor"` +// } +// +// func h(ctx context.Context, in *Input) (*Out, error) { +// if in.Cursor.IsSet { ... } +// } +// +// The spec emits the underlying T's schema (not the wrapper's), so the +// wire contract is identical to a plain query:"..." field: the only +// difference is server-side presence detection. This stays within +// engdocs/architecture/api-control-plane.md §3.5.1: the handler does +// not read undeclared URL keys, only its own declared field. + +import ( + "reflect" + + "github.com/danielgtaylor/huma/v2" +) + +// OptionalParam holds a query-parameter value and a flag indicating +// whether the client explicitly supplied the parameter. +type OptionalParam[T any] struct { + Value T + IsSet bool +} + +// Schema returns the schema for the wrapped type so the OpenAPI spec +// emits a plain T rather than a struct shape. +func (o OptionalParam[T]) Schema(r huma.Registry) *huma.Schema { + return huma.SchemaFromType(r, reflect.TypeOf(o.Value)) +} + +// Receiver exposes the Value field so Huma's parameter binder writes +// directly into it during request parsing. +func (o *OptionalParam[T]) Receiver() reflect.Value { + return reflect.ValueOf(o).Elem().Field(0) +} + +// OnParamSet is called by Huma after parsing; the isSet flag reflects +// whether the raw parameter was present in the request. +func (o *OptionalParam[T]) OnParamSet(isSet bool, _ any) { + o.IsSet = isSet +} diff --git a/internal/api/huma_spec_framework.go b/internal/api/huma_spec_framework.go new file mode 100644 index 0000000000..752434f043 --- /dev/null +++ b/internal/api/huma_spec_framework.go @@ -0,0 +1,82 @@ +package api + +// Framework-level OpenAPI decoration. +// +// Some wire contract spans every operation rather than any one input +// or output struct: the X-GC-Request-Id response header, written by +// withRequestID middleware on every response, is one such case. OpenAPI +// 3.1 has no mechanism to declare a header "globally"; the canonical +// pattern is to define the header once in components.headers and $ref +// it from each operation's responses (see +// speakeasy.com/openapi/responses/headers). +// +// registerFrameworkHeaders walks the registered OpenAPI document once +// after all routes are registered and adds the $ref entries. Handlers +// don't need to know or declare anything; the middleware remains the +// single source of enforcement, and the spec describes it accurately. + +import ( + "github.com/danielgtaylor/huma/v2" +) + +const ( + requestIDHeaderName = "X-GC-Request-Id" + requestIDHeaderRef = "#/components/headers/" + requestIDHeaderName + requestIDDescription = "Opaque per-response identifier assigned by the server for log correlation. Every response carries this header." +) + +// registerFrameworkHeaders registers reusable response headers in +// components.headers and adds $ref pointers to every registered +// operation's responses. Call once after all routes are registered. +func registerFrameworkHeaders(api huma.API) { + spec := api.OpenAPI() + if spec == nil { + return + } + if spec.Components == nil { + spec.Components = &huma.Components{} + } + if spec.Components.Headers == nil { + spec.Components.Headers = map[string]*huma.Header{} + } + if _, ok := spec.Components.Headers[requestIDHeaderName]; !ok { + spec.Components.Headers[requestIDHeaderName] = &huma.Header{ + Description: requestIDDescription, + Schema: &huma.Schema{ + Type: "string", + Description: requestIDDescription, + }, + } + } + if spec.Paths == nil { + return + } + for _, item := range spec.Paths { + if item == nil { + continue + } + ops := []*huma.Operation{ + item.Get, item.Put, item.Post, item.Patch, item.Delete, + item.Head, item.Options, item.Trace, + } + for _, op := range ops { + if op == nil || op.Responses == nil { + continue + } + for _, resp := range op.Responses { + if resp == nil { + continue + } + if resp.Headers == nil { + resp.Headers = map[string]*huma.Param{} + } + if _, ok := resp.Headers[requestIDHeaderName]; ok { + continue + } + resp.Headers[requestIDHeaderName] = &huma.Param{ + Ref: requestIDHeaderRef, + } + } + } + } +} diff --git a/internal/api/huma_sse_test.go b/internal/api/huma_sse_test.go index d4232e9d42..3278ffb521 100644 --- a/internal/api/huma_sse_test.go +++ b/internal/api/huma_sse_test.go @@ -2,8 +2,14 @@ package api import ( "encoding/json" + "net/http" + "net/http/httptest" + "reflect" + "sort" "strings" "testing" + + "github.com/gastownhall/gascity/internal/events" ) // TestEventStreamSchemaInSpec verifies that the events/stream endpoint @@ -84,3 +90,377 @@ func TestSSEEndpointsHaveSchemasInSpec(t *testing.T) { }) } } + +func TestEventStreamsUseTypedEnvelopeUnions(t *testing.T) { + tests := []struct { + path string + eventName string + wantRef string + }{ + { + path: "/v0/events/stream", + eventName: "tagged_event", + wantRef: "#/components/schemas/TypedTaggedEventStreamEnvelope", + }, + { + path: "/v0/city/{cityName}/events/stream", + eventName: "event", + wantRef: "#/components/schemas/TypedEventStreamEnvelope", + }, + } + + for _, source := range eventStreamSpecCases(t) { + t.Run(source.name, func(t *testing.T) { + for _, tt := range tests { + t.Run(tt.path, func(t *testing.T) { + gotRef := sseEventDataRef(t, source.spec, tt.path, tt.eventName) + if gotRef != tt.wantRef { + t.Fatalf("%s %s data ref = %q, want %q", tt.path, tt.eventName, gotRef, tt.wantRef) + } + }) + } + + schemas := componentSchemas(t, source.spec) + for _, name := range []string{ + "EventStreamEnvelope", + "TaggedEventStreamEnvelope", + "TypedEventStreamEnvelope", + "TypedTaggedEventStreamEnvelope", + } { + if _, ok := schemas[name]; !ok { + t.Fatalf("components.schemas missing %s", name) + } + } + }) + } +} + +func TestTypedEventEnvelopeUnionsCoverKnownEventTypes(t *testing.T) { + for _, source := range eventStreamSpecCases(t) { + t.Run(source.name, func(t *testing.T) { + for _, tc := range []struct { + schemaName string + cityField bool + }{ + {schemaName: "TypedEventStreamEnvelope"}, + {schemaName: "TypedTaggedEventStreamEnvelope", cityField: true}, + } { + t.Run(tc.schemaName, func(t *testing.T) { + assertTypedEventEnvelopeUnion(t, source.spec, tc.schemaName, tc.cityField) + }) + } + }) + } +} + +func eventStreamSpecCases(t *testing.T) []struct { + name string + spec map[string]any +} { + t.Helper() + return []struct { + name string + spec map[string]any + }{ + {name: "committed", spec: readCommittedOpenAPISpec(t)}, + {name: "live-supervisor", spec: readLiveSupervisorOpenAPISpec(t)}, + } +} + +func readLiveSupervisorOpenAPISpec(t *testing.T) map[string]any { + t.Helper() + + sm := newTestSupervisorMux(t, map[string]*fakeState{}) + req := httptest.NewRequest(http.MethodGet, "/openapi.json", nil) + rec := httptest.NewRecorder() + sm.ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Fatalf("GET /openapi.json status = %d, want 200; body=%s", rec.Code, rec.Body.String()) + } + var spec map[string]any + if err := json.Unmarshal(rec.Body.Bytes(), &spec); err != nil { + t.Fatalf("decode live openapi.json: %v", err) + } + return spec +} + +func sseEventDataRef(t *testing.T, spec map[string]any, path, eventName string) string { + t.Helper() + + paths, ok := spec["paths"].(map[string]any) + if !ok { + t.Fatal("OpenAPI paths missing") + } + pathItem, ok := paths[path].(map[string]any) + if !ok { + t.Fatalf("path %s missing", path) + } + get, ok := pathItem["get"].(map[string]any) + if !ok { + t.Fatalf("GET %s missing", path) + } + responses, ok := get["responses"].(map[string]any) + if !ok { + t.Fatalf("GET %s responses missing", path) + } + ok200, ok := responses["200"].(map[string]any) + if !ok { + t.Fatalf("GET %s 200 response missing", path) + } + content, ok := ok200["content"].(map[string]any) + if !ok { + t.Fatalf("GET %s 200 content missing", path) + } + stream, ok := content["text/event-stream"].(map[string]any) + if !ok { + t.Fatalf("GET %s text/event-stream content missing", path) + } + schema, ok := stream["schema"].(map[string]any) + if !ok { + t.Fatalf("GET %s stream schema missing", path) + } + items, ok := schema["items"].(map[string]any) + if !ok { + t.Fatalf("GET %s stream items schema missing", path) + } + oneOf, ok := items["oneOf"].([]any) + if !ok { + t.Fatalf("GET %s stream oneOf missing", path) + } + for _, branch := range oneOf { + branchSchema, ok := branch.(map[string]any) + if !ok { + continue + } + properties, ok := branchSchema["properties"].(map[string]any) + if !ok { + continue + } + eventProperty, ok := properties["event"].(map[string]any) + if !ok { + continue + } + if got, _ := eventProperty["const"].(string); got != eventName { + continue + } + dataProperty, ok := properties["data"].(map[string]any) + if !ok { + t.Fatalf("GET %s event %s data property missing", path, eventName) + } + ref, _ := dataProperty["$ref"].(string) + return ref + } + t.Fatalf("GET %s SSE event %s not found", path, eventName) + return "" +} + +func assertTypedEventEnvelopeUnion(t *testing.T, spec map[string]any, schemaName string, cityField bool) { + t.Helper() + + schemas := componentSchemas(t, spec) + union, ok := schemas[schemaName] + if !ok { + t.Fatalf("components.schemas missing %s", schemaName) + } + oneOf, ok := union["oneOf"].([]any) + if !ok { + t.Fatalf("%s oneOf missing", schemaName) + } + discriminator := typedEventDiscriminatorMapping(t, union, schemaName) + + expectedPayloadRefs := expectedEventPayloadRefs(t) + seen := map[string]int{} + for _, branch := range oneOf { + refObj, ok := branch.(map[string]any) + if !ok { + t.Fatalf("%s oneOf branch is not an object: %#v", schemaName, branch) + } + ref, _ := refObj["$ref"].(string) + if ref == "" { + t.Fatalf("%s oneOf branch missing $ref: %#v", schemaName, branch) + } + variant := schemaByRef(t, schemas, ref) + properties, ok := variant["properties"].(map[string]any) + if !ok { + t.Fatalf("%s variant %s properties missing", schemaName, ref) + } + typeProperty, ok := properties["type"].(map[string]any) + if !ok { + t.Fatalf("%s variant %s type property missing", schemaName, ref) + } + eventType := constOrSingleEnum(t, typeProperty) + wantPayloadRef, ok := expectedPayloadRefs[eventType] + if !ok { + t.Fatalf("%s variant %s has unknown event type %q", schemaName, ref, eventType) + } + seen[eventType]++ + if gotRef := discriminator[eventType]; gotRef != ref { + t.Fatalf("%s discriminator mapping for %s = %q, want %q", schemaName, eventType, gotRef, ref) + } + + payloadProperty, ok := properties["payload"].(map[string]any) + if !ok { + t.Fatalf("%s variant %s payload property missing", schemaName, ref) + } + gotPayloadRef, _ := payloadProperty["$ref"].(string) + if gotPayloadRef != wantPayloadRef { + t.Fatalf("%s variant %s payload ref = %q, want %q", schemaName, eventType, gotPayloadRef, wantPayloadRef) + } + + wantRequired := []string{"seq", "type", "ts", "actor", "payload"} + wantProperties := []string{"seq", "type", "ts", "actor", "subject", "message", "workflow", "payload"} + if cityField { + wantRequired = append(wantRequired, "city") + wantProperties = append(wantProperties, "city") + } + assertProperties(t, schemaName, eventType, properties, wantProperties) + assertRequiredFields(t, schemaName, eventType, variant, wantRequired) + } + + var missing, duplicate []string + for _, eventType := range events.KnownEventTypes { + switch seen[eventType] { + case 0: + missing = append(missing, eventType) + case 1: + default: + duplicate = append(duplicate, eventType) + } + } + sort.Strings(missing) + sort.Strings(duplicate) + if len(missing) > 0 || len(duplicate) > 0 { + t.Fatalf("%s event coverage mismatch; missing=%v duplicate=%v", schemaName, missing, duplicate) + } + if len(discriminator) != len(events.KnownEventTypes) { + t.Fatalf("%s discriminator mapping count = %d, want %d", schemaName, len(discriminator), len(events.KnownEventTypes)) + } + for eventType := range discriminator { + if seen[eventType] == 0 { + t.Fatalf("%s discriminator maps unknown event type %q", schemaName, eventType) + } + } +} + +func typedEventDiscriminatorMapping(t *testing.T, union map[string]any, schemaName string) map[string]string { + t.Helper() + + rawDiscriminator, ok := union["discriminator"].(map[string]any) + if !ok { + t.Fatalf("%s discriminator missing", schemaName) + } + if got, _ := rawDiscriminator["propertyName"].(string); got != "type" { + t.Fatalf("%s discriminator.propertyName = %q, want type", schemaName, got) + } + rawMapping, ok := rawDiscriminator["mapping"].(map[string]any) + if !ok { + t.Fatalf("%s discriminator.mapping missing", schemaName) + } + mapping := make(map[string]string, len(rawMapping)) + for eventType, rawRef := range rawMapping { + ref, ok := rawRef.(string) + if !ok || ref == "" { + t.Fatalf("%s discriminator mapping for %s is not a ref: %#v", schemaName, eventType, rawRef) + } + mapping[eventType] = ref + } + return mapping +} + +func componentSchemas(t *testing.T, spec map[string]any) map[string]map[string]any { + t.Helper() + + components, ok := spec["components"].(map[string]any) + if !ok { + t.Fatal("OpenAPI components missing") + } + rawSchemas, ok := components["schemas"].(map[string]any) + if !ok { + t.Fatal("OpenAPI components.schemas missing") + } + schemas := make(map[string]map[string]any, len(rawSchemas)) + for name, raw := range rawSchemas { + schema, ok := raw.(map[string]any) + if !ok { + continue + } + schemas[name] = schema + } + return schemas +} + +func expectedEventPayloadRefs(t *testing.T) map[string]string { + t.Helper() + + registered := events.RegisteredPayloadTypes() + expected := make(map[string]string, len(events.KnownEventTypes)) + for _, eventType := range events.KnownEventTypes { + sample, ok := registered[eventType] + if !ok { + t.Fatalf("%s payload not registered", eventType) + } + expected[eventType] = "#/components/schemas/" + reflect.TypeOf(sample).Name() + } + return expected +} + +func schemaByRef(t *testing.T, schemas map[string]map[string]any, ref string) map[string]any { + t.Helper() + + const prefix = "#/components/schemas/" + if !strings.HasPrefix(ref, prefix) { + t.Fatalf("schema ref %q does not have prefix %q", ref, prefix) + } + name := strings.TrimPrefix(ref, prefix) + schema, ok := schemas[name] + if !ok { + t.Fatalf("schema ref %q target missing", ref) + } + return schema +} + +func constOrSingleEnum(t *testing.T, schema map[string]any) string { + t.Helper() + + if value, ok := schema["const"].(string); ok { + return value + } + enum, ok := schema["enum"].([]any) + if !ok || len(enum) != 1 { + t.Fatalf("schema has neither string const nor single-value enum: %#v", schema) + } + value, ok := enum[0].(string) + if !ok { + t.Fatalf("schema single enum value is not a string: %#v", enum[0]) + } + return value +} + +func assertProperties(t *testing.T, schemaName, eventType string, properties map[string]any, fields []string) { + t.Helper() + + for _, field := range fields { + if _, ok := properties[field]; !ok { + t.Fatalf("%s variant %s missing property %q", schemaName, eventType, field) + } + } +} + +func assertRequiredFields(t *testing.T, schemaName, eventType string, schema map[string]any, fields []string) { + t.Helper() + + rawRequired, ok := schema["required"].([]any) + if !ok { + t.Fatalf("%s variant %s required fields missing", schemaName, eventType) + } + required := map[string]bool{} + for _, raw := range rawRequired { + name, _ := raw.(string) + required[name] = true + } + for _, field := range fields { + if !required[field] { + t.Fatalf("%s variant %s missing required field %q; required=%v", schemaName, eventType, field, rawRequired) + } + } +} diff --git a/internal/api/huma_types_beads.go b/internal/api/huma_types_beads.go index 79ea5ab180..e22488f6fb 100644 --- a/internal/api/huma_types_beads.go +++ b/internal/api/huma_types_beads.go @@ -53,13 +53,15 @@ type BeadCreateInput struct { CityScope IdempotencyKey string `header:"Idempotency-Key" required:"false" doc:"Idempotency key for safe retries."` Body struct { - Rig string `json:"rig,omitempty" doc:"Rig name."` - Title string `json:"title" doc:"Bead title." minLength:"1"` - Type string `json:"type,omitempty" doc:"Bead type."` - Priority *int `json:"priority,omitempty" doc:"Bead priority."` - Assignee string `json:"assignee,omitempty" doc:"Assigned agent."` - Description string `json:"description,omitempty" doc:"Bead description."` - Labels []string `json:"labels,omitempty" doc:"Bead labels."` + Rig string `json:"rig,omitempty" doc:"Rig name."` + Title string `json:"title" doc:"Bead title." minLength:"1"` + Type string `json:"type,omitempty" doc:"Bead type."` + Priority *int `json:"priority,omitempty" doc:"Bead priority."` + Assignee string `json:"assignee,omitempty" doc:"Assigned agent."` + Description string `json:"description,omitempty" doc:"Bead description."` + Labels []string `json:"labels,omitempty" doc:"Bead labels."` + Parent string `json:"parent,omitempty" doc:"Parent bead ID."` + Metadata map[string]string `json:"metadata,omitempty" doc:"Metadata key-value pairs to set at create time."` } } @@ -92,7 +94,9 @@ type beadUpdateBody struct { Description *string `json:"description,omitempty" doc:"Bead description."` Labels []string `json:"labels,omitempty" doc:"Bead labels."` RemoveLabels []string `json:"remove_labels,omitempty" doc:"Labels to remove."` + Parent *string `json:"parent,omitempty" nullable:"true" doc:"Parent bead ID. Use null or an empty string to clear."` Metadata map[string]string `json:"metadata,omitempty" doc:"Metadata key-value pairs to set."` + parentSet bool } // UnmarshalJSON rejects `"priority": null` explicitly. Standard Go JSON decoding @@ -116,6 +120,13 @@ func (b *beadUpdateBody) UnmarshalJSON(data []byte) error { return err } *b = beadUpdateBody(a) + if p, ok := raw["parent"]; ok { + b.parentSet = true + if bytes.Equal(bytes.TrimSpace(p), []byte("null")) { + parent := "" + b.Parent = &parent + } + } return nil } diff --git a/internal/api/huma_types_formulas.go b/internal/api/huma_types_formulas.go index 5e5101a70a..2dd2627b8f 100644 --- a/internal/api/huma_types_formulas.go +++ b/internal/api/huma_types_formulas.go @@ -138,7 +138,7 @@ type FormulaRunsInput struct { // their defaults. Callers that need to supply variable values use // POST /v0/city/{cityName}/formulas/{name}/preview (FormulaPreviewInput) // so the variable dictionary is a spec-visible typed body rather than -// a dynamic wildcard query scheme. See architecture.md §3.5.1. +// a dynamic wildcard query scheme. See engdocs/architecture/api-control-plane.md §3.5.1. type FormulaDetailInput struct { CityScope Name string `path:"name" doc:"Formula name."` @@ -170,7 +170,7 @@ func (i *FormulaDetailInput) Resolve(ctx huma.Context, _ *huma.PathBuffer) []err // input surface spec-visible. A prior revision accepted dynamic // var.* query parameters via a huma.Resolver; that scheme was // removed because OpenAPI 3.1 cannot describe wildcard query keys. -// See architecture.md §3.5.1. +// See engdocs/architecture/api-control-plane.md §3.5.1. type FormulaPreviewBody struct { ScopeKind string `json:"scope_kind,omitempty" doc:"Scope kind (city or rig)."` ScopeRef string `json:"scope_ref,omitempty" doc:"Scope reference."` diff --git a/internal/api/huma_types_patches.go b/internal/api/huma_types_patches.go index a5d7bfcbf4..d4215cb132 100644 --- a/internal/api/huma_types_patches.go +++ b/internal/api/huma_types_patches.go @@ -109,7 +109,9 @@ type ProviderPatchSetInput struct { Body struct { Name string `json:"name,omitempty" doc:"Provider name."` Command *string `json:"command,omitempty" doc:"Override command binary."` + ACPCommand *string `json:"acp_command,omitempty" doc:"Override ACP transport command binary."` Args []string `json:"args,omitempty" doc:"Override command arguments."` + ACPArgs []string `json:"acp_args,omitempty" doc:"Override ACP transport command arguments."` PromptMode *string `json:"prompt_mode,omitempty" doc:"Override prompt delivery mode."` PromptFlag *string `json:"prompt_flag,omitempty" doc:"Override prompt flag."` ReadyDelayMs *int `json:"ready_delay_ms,omitempty" doc:"Override ready delay in milliseconds."` diff --git a/internal/api/huma_types_providers.go b/internal/api/huma_types_providers.go index 49d8028b26..6b62b235b7 100644 --- a/internal/api/huma_types_providers.go +++ b/internal/api/huma_types_providers.go @@ -61,7 +61,9 @@ type ProviderCreateInput struct { DisplayName string `json:"display_name,omitempty" doc:"Human-readable display name."` Base *string `json:"base,omitempty" doc:"Optional provider base for inheritance."` Command string `json:"command,omitempty" doc:"Provider command binary. Omit for base-only descendants."` + ACPCommand string `json:"acp_command,omitempty" doc:"ACP transport command binary override."` Args []string `json:"args,omitempty" doc:"Command arguments."` + ACPArgs []string `json:"acp_args,omitempty" doc:"ACP transport command arguments override."` ArgsAppend []string `json:"args_append,omitempty" doc:"Arguments appended after inherited/base args."` PromptMode string `json:"prompt_mode,omitempty" doc:"Prompt delivery mode."` PromptFlag string `json:"prompt_flag,omitempty" doc:"Flag for prompt delivery."` @@ -79,7 +81,9 @@ type ProviderUpdateInput struct { DisplayName *string `json:"display_name,omitempty" doc:"Human-readable display name."` Base *string `json:"base,omitempty" doc:"Provider base for inheritance."` Command *string `json:"command,omitempty" doc:"Provider command binary."` + ACPCommand *string `json:"acp_command,omitempty" doc:"ACP transport command binary override."` Args []string `json:"args,omitempty" doc:"Command arguments."` + ACPArgs []string `json:"acp_args,omitempty" doc:"ACP transport command arguments override."` ArgsAppend []string `json:"args_append,omitempty" doc:"Arguments appended after inherited/base args."` PromptMode *string `json:"prompt_mode,omitempty" doc:"Prompt delivery mode."` PromptFlag *string `json:"prompt_flag,omitempty" doc:"Flag for prompt delivery."` diff --git a/internal/api/huma_types_sling.go b/internal/api/huma_types_sling.go index 05ea6d6097..19159e1b0e 100644 --- a/internal/api/huma_types_sling.go +++ b/internal/api/huma_types_sling.go @@ -23,5 +23,6 @@ type SlingInput struct { Vars map[string]string `json:"vars,omitempty" doc:"Formula variables."` ScopeKind string `json:"scope_kind,omitempty" doc:"Scope kind (city or rig)."` ScopeRef string `json:"scope_ref,omitempty" doc:"Scope reference."` + Force bool `json:"force,omitempty" doc:"Bypass cross-rig guards; for direct bead routes, also bypass missing-bead validation. Formula-backed graph routes may replace existing live workflow roots but still require the source bead to exist."` } } diff --git a/internal/api/openapi.json b/internal/api/openapi.json index ee1f1e44e7..dcac755149 100644 --- a/internal/api/openapi.json +++ b/internal/api/openapi.json @@ -1,5 +1,14 @@ { "components": { + "headers": { + "X-GC-Request-Id": { + "description": "Opaque per-response identifier assigned by the server for log correlation. Every response carries this header.", + "schema": { + "description": "Opaque per-response identifier assigned by the server for log correlation. Every response carries this header.", + "type": "string" + } + } + }, "schemas": { "AdapterCapabilities": { "additionalProperties": false, @@ -665,6 +674,15 @@ "AnnotatedProviderResponse": { "additionalProperties": false, "properties": { + "acp_args": { + "items": { + "type": "string" + }, + "type": "array" + }, + "acp_command": { + "type": "string" + }, "args": { "items": { "type": "string" @@ -818,6 +836,17 @@ "null" ] }, + "metadata": { + "additionalProperties": { + "type": "string" + }, + "description": "Metadata key-value pairs to set at create time.", + "type": "object" + }, + "parent": { + "description": "Parent bead ID.", + "type": "string" + }, "priority": { "description": "Bead priority.", "format": "int64", @@ -932,6 +961,13 @@ "description": "Metadata key-value pairs to set.", "type": "object" }, + "parent": { + "description": "Parent bead ID. Use null or an empty string to clear.", + "type": [ + "string", + "null" + ] + }, "priority": { "description": "Bead priority.", "format": "int64", @@ -1023,17 +1059,22 @@ "CityCreateResponse": { "additionalProperties": false, "properties": { + "name": { + "description": "Resolved city name as persisted in city.toml. Use this to filter the event stream for completion.", + "type": "string" + }, "ok": { - "description": "True on success.", + "description": "True when scaffolding + registration succeeded. Does not imply the city is ready yet; watch /v0/events/stream for city.ready.", "type": "boolean" }, "path": { - "description": "Resolved absolute path of the created city.", + "description": "Resolved absolute path of the created city directory.", "type": "string" } }, "required": [ "ok", + "name", "path" ], "type": "object" @@ -1117,6 +1158,34 @@ ], "type": "object" }, + "CityLifecyclePayload": { + "additionalProperties": false, + "properties": { + "error": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "phases_completed": { + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + "required": [ + "name", + "path" + ], + "type": "object" + }, "CityPatchInputBody": { "additionalProperties": false, "properties": { @@ -1127,6 +1196,29 @@ }, "type": "object" }, + "CityUnregisterResponse": { + "additionalProperties": false, + "properties": { + "name": { + "description": "Resolved registry name. Filter the event stream by this to observe completion.", + "type": "string" + }, + "ok": { + "description": "True when the registry entry was removed and the supervisor was signaled. Does not imply the city's controller has stopped yet; watch /v0/events/stream for city.unregistered.", + "type": "boolean" + }, + "path": { + "description": "Resolved absolute city directory. The directory itself is not modified; unregister only affects the supervisor's registry.", + "type": "string" + } + }, + "required": [ + "ok", + "name", + "path" + ], + "type": "object" + }, "ConfigAgentResponse": { "additionalProperties": false, "properties": { @@ -1796,10 +1888,16 @@ "default": "about:blank", "description": "A URI reference to human-readable documentation for the error.", "examples": [ - "https://example.com/errors/example" + "https://example.com/errors/example", + "urn:gascity:error:sling-missing-bead", + "urn:gascity:error:sling-cross-rig" ], "format": "uri", - "type": "string" + "type": "string", + "x-gascity-problem-types": [ + "urn:gascity:error:sling-missing-bead", + "urn:gascity:error:sling-cross-rig" + ] } }, "type": "object" @@ -1859,6 +1957,9 @@ { "$ref": "#/components/schemas/BoundEventPayload" }, + { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, { "$ref": "#/components/schemas/GroupCreatedEventPayload" }, @@ -4381,6 +4482,20 @@ "ProviderCreateInputBody": { "additionalProperties": false, "properties": { + "acp_args": { + "description": "ACP transport command arguments override.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "acp_command": { + "description": "ACP transport command binary override.", + "type": "string" + }, "args": { "description": "Command arguments.", "items": { @@ -4506,6 +4621,21 @@ "ProviderPatch": { "additionalProperties": false, "properties": { + "ACPArgs": { + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "ACPCommand": { + "type": [ + "string", + "null" + ] + }, "Args": { "items": { "type": "string" @@ -4587,7 +4717,9 @@ "Name", "Base", "Command", + "ACPCommand", "Args", + "ACPArgs", "ArgsAppend", "OptionsSchemaMerge", "PromptMode", @@ -4602,6 +4734,20 @@ "ProviderPatchSetInputBody": { "additionalProperties": false, "properties": { + "acp_args": { + "description": "Override ACP transport command arguments.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "acp_command": { + "description": "Override ACP transport command binary.", + "type": "string" + }, "args": { "description": "Override command arguments.", "items": { @@ -4747,6 +4893,15 @@ "ProviderResponse": { "additionalProperties": false, "properties": { + "acp_args": { + "items": { + "type": "string" + }, + "type": "array" + }, + "acp_command": { + "type": "string" + }, "args": { "items": { "type": "string" @@ -4798,6 +4953,15 @@ "ProviderSpecJSON": { "additionalProperties": false, "properties": { + "acp_args": { + "items": { + "type": "string" + }, + "type": "array" + }, + "acp_command": { + "type": "string" + }, "args": { "items": { "type": "string" @@ -4835,6 +4999,20 @@ "ProviderUpdateInputBody": { "additionalProperties": false, "properties": { + "acp_args": { + "description": "ACP transport command arguments override.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "acp_command": { + "description": "ACP transport command binary override.", + "type": "string" + }, "args": { "description": "Command arguments.", "items": { @@ -5825,6 +6003,10 @@ "description": "Bead ID to sling.", "type": "string" }, + "force": { + "description": "Bypass cross-rig guards; for direct bead routes, also bypass missing-bead validation. Formula-backed graph routes may replace existing live workflow roots but still require the source bead to exist.", + "type": "boolean" + }, "formula": { "description": "Formula name for workflow launch.", "type": "string" @@ -6335,24 +6517,190 @@ ], "type": "string" }, - "UnboundEventPayload": { - "additionalProperties": false, - "properties": { - "count": { - "format": "int64", - "type": "integer" + "TypedEventStreamEnvelope": { + "description": "Discriminated union of city event stream envelopes. Each variant constrains the envelope type and payload schema together.", + "discriminator": { + "mapping": { + "bead.closed": "#/components/schemas/TypedEventStreamEnvelopeBeadClosed", + "bead.created": "#/components/schemas/TypedEventStreamEnvelopeBeadCreated", + "bead.updated": "#/components/schemas/TypedEventStreamEnvelopeBeadUpdated", + "city.created": "#/components/schemas/TypedEventStreamEnvelopeCityCreated", + "city.init_failed": "#/components/schemas/TypedEventStreamEnvelopeCityInitFailed", + "city.ready": "#/components/schemas/TypedEventStreamEnvelopeCityReady", + "city.resumed": "#/components/schemas/TypedEventStreamEnvelopeCityResumed", + "city.suspended": "#/components/schemas/TypedEventStreamEnvelopeCitySuspended", + "city.unregister_failed": "#/components/schemas/TypedEventStreamEnvelopeCityUnregisterFailed", + "city.unregister_requested": "#/components/schemas/TypedEventStreamEnvelopeCityUnregisterRequested", + "city.unregistered": "#/components/schemas/TypedEventStreamEnvelopeCityUnregistered", + "controller.started": "#/components/schemas/TypedEventStreamEnvelopeControllerStarted", + "controller.stopped": "#/components/schemas/TypedEventStreamEnvelopeControllerStopped", + "convoy.closed": "#/components/schemas/TypedEventStreamEnvelopeConvoyClosed", + "convoy.created": "#/components/schemas/TypedEventStreamEnvelopeConvoyCreated", + "extmsg.adapter_added": "#/components/schemas/TypedEventStreamEnvelopeExtmsgAdapterAdded", + "extmsg.adapter_removed": "#/components/schemas/TypedEventStreamEnvelopeExtmsgAdapterRemoved", + "extmsg.bound": "#/components/schemas/TypedEventStreamEnvelopeExtmsgBound", + "extmsg.group_created": "#/components/schemas/TypedEventStreamEnvelopeExtmsgGroupCreated", + "extmsg.inbound": "#/components/schemas/TypedEventStreamEnvelopeExtmsgInbound", + "extmsg.outbound": "#/components/schemas/TypedEventStreamEnvelopeExtmsgOutbound", + "extmsg.unbound": "#/components/schemas/TypedEventStreamEnvelopeExtmsgUnbound", + "mail.archived": "#/components/schemas/TypedEventStreamEnvelopeMailArchived", + "mail.deleted": "#/components/schemas/TypedEventStreamEnvelopeMailDeleted", + "mail.marked_read": "#/components/schemas/TypedEventStreamEnvelopeMailMarkedRead", + "mail.marked_unread": "#/components/schemas/TypedEventStreamEnvelopeMailMarkedUnread", + "mail.read": "#/components/schemas/TypedEventStreamEnvelopeMailRead", + "mail.replied": "#/components/schemas/TypedEventStreamEnvelopeMailReplied", + "mail.sent": "#/components/schemas/TypedEventStreamEnvelopeMailSent", + "order.completed": "#/components/schemas/TypedEventStreamEnvelopeOrderCompleted", + "order.failed": "#/components/schemas/TypedEventStreamEnvelopeOrderFailed", + "order.fired": "#/components/schemas/TypedEventStreamEnvelopeOrderFired", + "provider.swapped": "#/components/schemas/TypedEventStreamEnvelopeProviderSwapped", + "session.crashed": "#/components/schemas/TypedEventStreamEnvelopeSessionCrashed", + "session.draining": "#/components/schemas/TypedEventStreamEnvelopeSessionDraining", + "session.idle_killed": "#/components/schemas/TypedEventStreamEnvelopeSessionIdleKilled", + "session.quarantined": "#/components/schemas/TypedEventStreamEnvelopeSessionQuarantined", + "session.stopped": "#/components/schemas/TypedEventStreamEnvelopeSessionStopped", + "session.suspended": "#/components/schemas/TypedEventStreamEnvelopeSessionSuspended", + "session.undrained": "#/components/schemas/TypedEventStreamEnvelopeSessionUndrained", + "session.updated": "#/components/schemas/TypedEventStreamEnvelopeSessionUpdated", + "session.woke": "#/components/schemas/TypedEventStreamEnvelopeSessionWoke", + "worker.operation": "#/components/schemas/TypedEventStreamEnvelopeWorkerOperation" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeBeadClosed" }, - "session_id": { - "type": "string" + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeBeadCreated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeBeadUpdated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityCreated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityInitFailed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityReady" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityResumed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCitySuspended" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityUnregisterFailed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityUnregisterRequested" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeCityUnregistered" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeControllerStarted" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeControllerStopped" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeConvoyClosed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeConvoyCreated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgAdapterAdded" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgAdapterRemoved" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgBound" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgGroupCreated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgInbound" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgOutbound" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeExtmsgUnbound" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailArchived" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailDeleted" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailMarkedRead" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailMarkedUnread" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailRead" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailReplied" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeMailSent" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeOrderCompleted" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeOrderFailed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeOrderFired" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeProviderSwapped" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionCrashed" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionDraining" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionIdleKilled" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionQuarantined" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionStopped" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionSuspended" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionUndrained" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionUpdated" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeSessionWoke" + }, + { + "$ref": "#/components/schemas/TypedEventStreamEnvelopeWorkerOperation" } - }, - "required": [ - "session_id", - "count" ], - "type": "object" + "title": "Typed city event stream envelope" }, - "WireEvent": { + "TypedEventStreamEnvelopeBeadClosed": { "additionalProperties": false, "properties": { "actor": { @@ -6362,7 +6710,7 @@ "type": "string" }, "payload": { - "$ref": "#/components/schemas/EventPayload" + "$ref": "#/components/schemas/BeadEventPayload" }, "seq": { "format": "int64", @@ -6377,31 +6725,34 @@ "type": "string" }, "type": { + "const": "bead.closed", "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" } }, "required": [ "seq", "type", "ts", - "actor" + "actor", + "payload" ], + "title": "TypedEventStreamEnvelope bead.closed", "type": "object" }, - "WireTaggedEvent": { + "TypedEventStreamEnvelopeBeadCreated": { "additionalProperties": false, "properties": { "actor": { "type": "string" }, - "city": { - "type": "string" - }, "message": { "type": "string" }, "payload": { - "$ref": "#/components/schemas/EventPayload" + "$ref": "#/components/schemas/BeadEventPayload" }, "seq": { "format": "int64", @@ -6416,23 +6767,4004 @@ "type": "string" }, "type": { + "const": "bead.created", "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" } }, "required": [ - "city", "seq", "type", "ts", - "actor" + "actor", + "payload" ], + "title": "TypedEventStreamEnvelope bead.created", "type": "object" }, - "WorkerOperationEventPayload": { + "TypedEventStreamEnvelopeBeadUpdated": { "additionalProperties": false, "properties": { - "delivered": { - "type": "boolean" + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BeadEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "bead.updated", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope bead.updated", + "type": "object" + }, + "TypedEventStreamEnvelopeCityCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.created", + "type": "object" + }, + "TypedEventStreamEnvelopeCityInitFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.init_failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.init_failed", + "type": "object" + }, + "TypedEventStreamEnvelopeCityReady": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.ready", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.ready", + "type": "object" + }, + "TypedEventStreamEnvelopeCityResumed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.resumed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.resumed", + "type": "object" + }, + "TypedEventStreamEnvelopeCitySuspended": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.suspended", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.suspended", + "type": "object" + }, + "TypedEventStreamEnvelopeCityUnregisterFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregister_failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.unregister_failed", + "type": "object" + }, + "TypedEventStreamEnvelopeCityUnregisterRequested": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregister_requested", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.unregister_requested", + "type": "object" + }, + "TypedEventStreamEnvelopeCityUnregistered": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregistered", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope city.unregistered", + "type": "object" + }, + "TypedEventStreamEnvelopeControllerStarted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "controller.started", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope controller.started", + "type": "object" + }, + "TypedEventStreamEnvelopeControllerStopped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "controller.stopped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope controller.stopped", + "type": "object" + }, + "TypedEventStreamEnvelopeConvoyClosed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "convoy.closed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope convoy.closed", + "type": "object" + }, + "TypedEventStreamEnvelopeConvoyCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "convoy.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope convoy.created", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgAdapterAdded": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/AdapterEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.adapter_added", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.adapter_added", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgAdapterRemoved": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/AdapterEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.adapter_removed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.adapter_removed", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgBound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BoundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.bound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.bound", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgGroupCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/GroupCreatedEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.group_created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.group_created", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgInbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/InboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.inbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.inbound", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgOutbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/OutboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.outbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.outbound", + "type": "object" + }, + "TypedEventStreamEnvelopeExtmsgUnbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/UnboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.unbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope extmsg.unbound", + "type": "object" + }, + "TypedEventStreamEnvelopeMailArchived": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.archived", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.archived", + "type": "object" + }, + "TypedEventStreamEnvelopeMailDeleted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.deleted", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.deleted", + "type": "object" + }, + "TypedEventStreamEnvelopeMailMarkedRead": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.marked_read", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.marked_read", + "type": "object" + }, + "TypedEventStreamEnvelopeMailMarkedUnread": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.marked_unread", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.marked_unread", + "type": "object" + }, + "TypedEventStreamEnvelopeMailRead": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.read", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.read", + "type": "object" + }, + "TypedEventStreamEnvelopeMailReplied": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.replied", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.replied", + "type": "object" + }, + "TypedEventStreamEnvelopeMailSent": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.sent", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope mail.sent", + "type": "object" + }, + "TypedEventStreamEnvelopeOrderCompleted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.completed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope order.completed", + "type": "object" + }, + "TypedEventStreamEnvelopeOrderFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope order.failed", + "type": "object" + }, + "TypedEventStreamEnvelopeOrderFired": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.fired", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope order.fired", + "type": "object" + }, + "TypedEventStreamEnvelopeProviderSwapped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "provider.swapped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope provider.swapped", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionCrashed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.crashed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.crashed", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionDraining": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.draining", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.draining", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionIdleKilled": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.idle_killed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.idle_killed", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionQuarantined": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.quarantined", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.quarantined", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionStopped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.stopped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.stopped", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionSuspended": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.suspended", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.suspended", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionUndrained": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.undrained", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.undrained", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionUpdated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.updated", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.updated", + "type": "object" + }, + "TypedEventStreamEnvelopeSessionWoke": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.woke", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope session.woke", + "type": "object" + }, + "TypedEventStreamEnvelopeWorkerOperation": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/WorkerOperationEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "worker.operation", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload" + ], + "title": "TypedEventStreamEnvelope worker.operation", + "type": "object" + }, + "TypedTaggedEventStreamEnvelope": { + "description": "Discriminated union of supervisor event stream envelopes. Each variant constrains the envelope type and payload schema together and includes the source city.", + "discriminator": { + "mapping": { + "bead.closed": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadClosed", + "bead.created": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadCreated", + "bead.updated": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadUpdated", + "city.created": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityCreated", + "city.init_failed": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityInitFailed", + "city.ready": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityReady", + "city.resumed": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityResumed", + "city.suspended": "#/components/schemas/TypedTaggedEventStreamEnvelopeCitySuspended", + "city.unregister_failed": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregisterFailed", + "city.unregister_requested": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregisterRequested", + "city.unregistered": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregistered", + "controller.started": "#/components/schemas/TypedTaggedEventStreamEnvelopeControllerStarted", + "controller.stopped": "#/components/schemas/TypedTaggedEventStreamEnvelopeControllerStopped", + "convoy.closed": "#/components/schemas/TypedTaggedEventStreamEnvelopeConvoyClosed", + "convoy.created": "#/components/schemas/TypedTaggedEventStreamEnvelopeConvoyCreated", + "extmsg.adapter_added": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded", + "extmsg.adapter_removed": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved", + "extmsg.bound": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgBound", + "extmsg.group_created": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgGroupCreated", + "extmsg.inbound": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgInbound", + "extmsg.outbound": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgOutbound", + "extmsg.unbound": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgUnbound", + "mail.archived": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailArchived", + "mail.deleted": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailDeleted", + "mail.marked_read": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailMarkedRead", + "mail.marked_unread": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailMarkedUnread", + "mail.read": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailRead", + "mail.replied": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailReplied", + "mail.sent": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailSent", + "order.completed": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderCompleted", + "order.failed": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderFailed", + "order.fired": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderFired", + "provider.swapped": "#/components/schemas/TypedTaggedEventStreamEnvelopeProviderSwapped", + "session.crashed": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionCrashed", + "session.draining": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionDraining", + "session.idle_killed": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionIdleKilled", + "session.quarantined": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionQuarantined", + "session.stopped": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionStopped", + "session.suspended": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionSuspended", + "session.undrained": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionUndrained", + "session.updated": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionUpdated", + "session.woke": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionWoke", + "worker.operation": "#/components/schemas/TypedTaggedEventStreamEnvelopeWorkerOperation" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadClosed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadCreated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeBeadUpdated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityCreated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityInitFailed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityReady" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityResumed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCitySuspended" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregisterFailed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregisterRequested" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeCityUnregistered" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeControllerStarted" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeControllerStopped" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeConvoyClosed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeConvoyCreated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgBound" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgGroupCreated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgInbound" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgOutbound" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeExtmsgUnbound" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailArchived" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailDeleted" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailMarkedRead" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailMarkedUnread" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailRead" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailReplied" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeMailSent" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderCompleted" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderFailed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeOrderFired" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeProviderSwapped" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionCrashed" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionDraining" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionIdleKilled" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionQuarantined" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionStopped" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionSuspended" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionUndrained" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionUpdated" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeSessionWoke" + }, + { + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelopeWorkerOperation" + } + ], + "title": "Typed supervisor event stream envelope" + }, + "TypedTaggedEventStreamEnvelopeBeadClosed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BeadEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "bead.closed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope bead.closed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeBeadCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BeadEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "bead.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope bead.created", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeBeadUpdated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BeadEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "bead.updated", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope bead.updated", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.created", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityInitFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.init_failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.init_failed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityReady": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.ready", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.ready", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityResumed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.resumed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.resumed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCitySuspended": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.suspended", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.suspended", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityUnregisterFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregister_failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.unregister_failed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityUnregisterRequested": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregister_requested", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.unregister_requested", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeCityUnregistered": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/CityLifecyclePayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "city.unregistered", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope city.unregistered", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeControllerStarted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "controller.started", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope controller.started", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeControllerStopped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "controller.stopped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope controller.stopped", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeConvoyClosed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "convoy.closed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope convoy.closed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeConvoyCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "convoy.created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope convoy.created", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/AdapterEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.adapter_added", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.adapter_added", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/AdapterEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.adapter_removed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.adapter_removed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgBound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/BoundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.bound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.bound", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgGroupCreated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/GroupCreatedEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.group_created", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.group_created", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgInbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/InboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.inbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.inbound", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgOutbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/OutboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.outbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.outbound", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeExtmsgUnbound": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/UnboundEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "extmsg.unbound", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope extmsg.unbound", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailArchived": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.archived", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.archived", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailDeleted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.deleted", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.deleted", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailMarkedRead": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.marked_read", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.marked_read", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailMarkedUnread": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.marked_unread", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.marked_unread", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailRead": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.read", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.read", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailReplied": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.replied", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.replied", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeMailSent": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/MailEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "mail.sent", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope mail.sent", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeOrderCompleted": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.completed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope order.completed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeOrderFailed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.failed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope order.failed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeOrderFired": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "order.fired", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope order.fired", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeProviderSwapped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "provider.swapped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope provider.swapped", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionCrashed": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.crashed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.crashed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionDraining": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.draining", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.draining", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionIdleKilled": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.idle_killed", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.idle_killed", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionQuarantined": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.quarantined", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.quarantined", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionStopped": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.stopped", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.stopped", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionSuspended": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.suspended", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.suspended", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionUndrained": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.undrained", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.undrained", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionUpdated": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.updated", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.updated", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeSessionWoke": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/NoPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "session.woke", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope session.woke", + "type": "object" + }, + "TypedTaggedEventStreamEnvelopeWorkerOperation": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/WorkerOperationEventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "const": "worker.operation", + "type": "string" + }, + "workflow": { + "$ref": "#/components/schemas/WorkflowEventProjection" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor", + "payload", + "city" + ], + "title": "TypedTaggedEventStreamEnvelope worker.operation", + "type": "object" + }, + "UnboundEventPayload": { + "additionalProperties": false, + "properties": { + "count": { + "format": "int64", + "type": "integer" + }, + "session_id": { + "type": "string" + } + }, + "required": [ + "session_id", + "count" + ], + "type": "object" + }, + "WireEvent": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/EventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "seq", + "type", + "ts", + "actor" + ], + "type": "object" + }, + "WireTaggedEvent": { + "additionalProperties": false, + "properties": { + "actor": { + "type": "string" + }, + "city": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/components/schemas/EventPayload" + }, + "seq": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "subject": { + "type": "string" + }, + "ts": { + "format": "date-time", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "city", + "seq", + "type", + "ts", + "actor" + ], + "type": "object" + }, + "WorkerOperationEventPayload": { + "additionalProperties": false, + "properties": { + "delivered": { + "type": "boolean" }, "duration_ms": { "format": "int64", @@ -6852,7 +11184,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -6862,7 +11199,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get health" @@ -6880,7 +11222,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -6890,7 +11237,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 cities" @@ -6899,6 +11251,19 @@ "/v0/city": { "post": { "operationId": "post-v0-city", + "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + } + ], "requestBody": { "content": { "application/json": { @@ -6910,7 +11275,7 @@ "required": true }, "responses": { - "200": { + "202": { "content": { "application/json": { "schema": { @@ -6918,7 +11283,12 @@ } } }, - "description": "OK" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -6928,7 +11298,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city" @@ -6960,7 +11335,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -6970,7 +11350,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name" @@ -6978,6 +11363,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7010,7 +11406,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7020,7 +11421,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name" @@ -7030,6 +11436,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-agent-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7062,7 +11479,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7072,7 +11494,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name agent by base" @@ -7121,6 +11548,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -7132,7 +11562,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agent by base" @@ -7140,6 +11575,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-agent-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7182,7 +11628,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7192,7 +11643,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name agent by base" @@ -7254,7 +11710,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7264,7 +11725,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agent by base output" @@ -7365,7 +11831,19 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "GC-Agent-Status": { + "description": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "schema": { + "description": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "type": "string" + } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7375,7 +11853,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream agent output in real time" @@ -7385,6 +11868,17 @@ "post": { "operationId": "post-v0-city-by-city-name-agent-by-base-by-action", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7431,7 +11925,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7441,7 +11940,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name agent by base by action" @@ -7451,6 +11955,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-agent-by-dir-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7493,7 +12008,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7503,7 +12023,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name agent by dir by base" @@ -7562,6 +12087,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -7573,7 +12101,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agent by dir by base" @@ -7581,6 +12114,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-agent-by-dir-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7633,7 +12177,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7643,7 +12192,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name agent by dir by base" @@ -7715,7 +12269,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7725,7 +12284,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agent by dir by base output" @@ -7836,7 +12400,19 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "GC-Agent-Status": { + "description": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "schema": { + "description": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "type": "string" + } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7846,7 +12422,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream agent output in real time (qualified name)" @@ -7856,6 +12437,17 @@ "post": { "operationId": "post-v0-city-by-city-name-agent-by-dir-by-base-by-action", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -7912,7 +12504,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -7922,7 +12519,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name agent by dir by base by action" @@ -8027,6 +12629,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8038,7 +12643,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name agents" @@ -8046,6 +12656,17 @@ "post": { "operationId": "create-agent", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8078,7 +12699,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8088,7 +12714,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create an agent" @@ -8098,6 +12729,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-bead-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8130,7 +12772,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8140,7 +12787,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name bead by ID" @@ -8189,6 +12841,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8200,7 +12855,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name bead by ID" @@ -8208,6 +12868,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-bead-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8250,7 +12921,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8260,7 +12936,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name bead by ID" @@ -8270,6 +12951,17 @@ "post": { "operationId": "post-v0-city-by-city-name-bead-by-id-assign", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8324,6 +13016,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8335,7 +13030,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name bead by ID assign" @@ -8345,6 +13045,17 @@ "post": { "operationId": "post-v0-city-by-city-name-bead-by-id-close", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8377,7 +13088,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8387,7 +13103,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name bead by ID close" @@ -8438,6 +13159,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8449,7 +13173,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name bead by ID deps" @@ -8459,6 +13188,17 @@ "post": { "operationId": "post-v0-city-by-city-name-bead-by-id-reopen", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8491,7 +13231,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8501,7 +13246,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name bead by ID reopen" @@ -8511,6 +13261,17 @@ "post": { "operationId": "post-v0-city-by-city-name-bead-by-id-update", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8553,7 +13314,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -8563,7 +13329,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name bead by ID update" @@ -8696,6 +13467,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8707,7 +13481,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name beads" @@ -8715,6 +13494,17 @@ "post": { "operationId": "create-bead", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -8765,6 +13555,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8776,7 +13569,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a bead" @@ -8827,6 +13625,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8838,7 +13639,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name beads graph by root ID" @@ -8899,6 +13705,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8910,7 +13719,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name beads ready" @@ -8951,6 +13765,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -8962,7 +13779,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name config" @@ -9003,6 +13825,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9014,7 +13839,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name config explain" @@ -9046,7 +13876,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9056,7 +13891,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name config validate" @@ -9066,6 +13906,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-convoy-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9098,7 +13949,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9108,7 +13964,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name convoy by ID" @@ -9157,6 +14018,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9168,7 +14032,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name convoy by ID" @@ -9178,6 +14047,17 @@ "post": { "operationId": "post-v0-city-by-city-name-convoy-by-id-add", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9220,7 +14100,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9230,7 +14115,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name convoy by ID add" @@ -9281,6 +14171,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9292,7 +14185,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name convoy by ID check" @@ -9302,6 +14200,17 @@ "post": { "operationId": "post-v0-city-by-city-name-convoy-by-id-close", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9334,7 +14243,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9344,7 +14258,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name convoy by ID close" @@ -9354,6 +14273,17 @@ "post": { "operationId": "post-v0-city-by-city-name-convoy-by-id-remove", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9396,7 +14326,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9406,7 +14341,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name convoy by ID remove" @@ -9489,6 +14429,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9500,7 +14443,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name convoys" @@ -9508,6 +14456,17 @@ "post": { "operationId": "create-convoy", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9549,6 +14508,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9560,7 +14522,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a convoy" @@ -9673,6 +14640,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9684,7 +14654,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name events" @@ -9692,6 +14667,17 @@ "post": { "operationId": "emit-event", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9724,7 +14710,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9734,7 +14725,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Emit an event" @@ -9788,7 +14784,7 @@ { "properties": { "data": { - "$ref": "#/components/schemas/EventStreamEnvelope" + "$ref": "#/components/schemas/TypedEventStreamEnvelope" }, "event": { "const": "event", @@ -9844,7 +14840,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9854,7 +14855,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream city events in real time" @@ -9864,6 +14870,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-extmsg-adapters", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9896,7 +14913,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -9906,7 +14928,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name extmsg adapters" @@ -9945,6 +14972,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -9956,7 +14986,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name extmsg adapters" @@ -9964,6 +14999,17 @@ "post": { "operationId": "register-extmsg-adapter", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -9996,7 +15042,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10006,7 +15057,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Register an external messaging adapter" @@ -10016,6 +15072,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-bind", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10048,7 +15115,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10058,7 +15130,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg bind" @@ -10109,6 +15186,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -10120,7 +15200,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name extmsg bindings" @@ -10202,7 +15287,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10212,7 +15302,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name extmsg groups" @@ -10220,6 +15315,17 @@ "post": { "operationId": "ensure-extmsg-group", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10252,7 +15358,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10262,7 +15373,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Ensure an external messaging group exists" @@ -10272,6 +15388,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-inbound", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10304,7 +15431,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10314,7 +15446,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg inbound" @@ -10324,6 +15461,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-outbound", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10356,7 +15504,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10366,7 +15519,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg outbound" @@ -10376,6 +15534,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-extmsg-participants", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10408,7 +15577,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10418,7 +15592,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name extmsg participants" @@ -10426,6 +15605,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-participants", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10458,7 +15648,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10468,7 +15663,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg participants" @@ -10569,6 +15769,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -10580,7 +15783,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name extmsg transcript" @@ -10590,6 +15798,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-transcript-ack", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10622,7 +15841,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10632,7 +15856,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg transcript ack" @@ -10642,6 +15871,17 @@ "post": { "operationId": "post-v0-city-by-city-name-extmsg-unbind", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -10674,7 +15914,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10684,7 +15929,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name extmsg unbind" @@ -10757,7 +16007,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10767,7 +16022,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formula by name" @@ -10819,7 +16079,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10829,7 +16094,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formulas" @@ -10893,7 +16163,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10903,7 +16178,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formulas feed" @@ -10976,7 +16256,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -10986,7 +16271,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formulas by name" @@ -10996,6 +16286,17 @@ "post": { "operationId": "post-v0-city-by-city-name-formulas-by-name-preview", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11038,7 +16339,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11048,7 +16354,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name formulas by name preview" @@ -11124,7 +16435,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11134,7 +16450,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name formulas by name runs" @@ -11166,7 +16487,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11176,7 +16502,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name health" @@ -11289,6 +16620,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11300,7 +16634,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name mail" @@ -11308,6 +16647,17 @@ "post": { "operationId": "send-mail", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11358,6 +16708,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11369,7 +16722,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Send a mail message" @@ -11421,7 +16779,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11431,7 +16794,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name mail count" @@ -11492,6 +16860,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11503,7 +16874,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name mail thread by ID" @@ -11513,6 +16889,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-mail-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11555,7 +16942,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11565,7 +16957,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name mail by ID" @@ -11624,6 +17021,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11635,7 +17035,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name mail by ID" @@ -11645,6 +17050,17 @@ "post": { "operationId": "post-v0-city-by-city-name-mail-by-id-archive", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11687,7 +17103,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11697,7 +17118,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name mail by ID archive" @@ -11707,6 +17133,17 @@ "post": { "operationId": "post-v0-city-by-city-name-mail-by-id-mark-unread", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11749,7 +17186,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11759,7 +17201,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name mail by ID mark unread" @@ -11769,6 +17216,17 @@ "post": { "operationId": "post-v0-city-by-city-name-mail-by-id-read", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11811,7 +17269,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11821,7 +17284,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name mail by ID read" @@ -11831,6 +17299,17 @@ "post": { "operationId": "reply-mail", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -11892,6 +17371,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -11903,7 +17385,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Reply to a mail message" @@ -11955,7 +17442,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -11965,7 +17457,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name order history by bead ID" @@ -12007,7 +17504,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12017,7 +17519,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name order by name" @@ -12027,6 +17534,17 @@ "post": { "operationId": "post-v0-city-by-city-name-order-by-name-disable", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12059,7 +17577,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12069,7 +17592,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name order by name disable" @@ -12079,6 +17607,17 @@ "post": { "operationId": "post-v0-city-by-city-name-order-by-name-enable", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12111,7 +17650,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12121,7 +17665,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name order by name enable" @@ -12153,7 +17702,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12163,7 +17717,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name orders" @@ -12195,7 +17754,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12205,7 +17769,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name orders check" @@ -12269,7 +17838,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12279,7 +17853,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name orders feed" @@ -12345,7 +17924,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12355,7 +17939,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name orders history" @@ -12387,7 +17976,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12397,7 +17991,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name packs" @@ -12407,6 +18006,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-patches-agent-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12439,7 +18049,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12449,7 +18064,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name patches agent by base" @@ -12498,6 +18118,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12509,7 +18132,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches agent by base" @@ -12519,6 +18147,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-patches-agent-by-dir-by-base", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12561,7 +18200,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12571,7 +18215,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name patches agent by dir by base" @@ -12630,6 +18279,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12641,7 +18293,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches agent by dir by base" @@ -12682,6 +18339,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12693,7 +18353,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches agents" @@ -12701,6 +18366,17 @@ "put": { "operationId": "put-v0-city-by-city-name-patches-agents", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12733,7 +18409,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12743,7 +18424,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Put v0 city by city name patches agents" @@ -12753,6 +18439,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-patches-provider-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12785,7 +18482,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12795,7 +18497,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name patches provider by name" @@ -12844,6 +18551,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12855,7 +18565,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches provider by name" @@ -12896,6 +18611,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -12907,7 +18625,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches providers" @@ -12915,6 +18638,17 @@ "put": { "operationId": "put-v0-city-by-city-name-patches-providers", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12947,7 +18681,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -12957,7 +18696,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Put v0 city by city name patches providers" @@ -12967,6 +18711,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-patches-rig-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -12999,7 +18754,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13009,7 +18769,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name patches rig by name" @@ -13058,6 +18823,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13069,7 +18837,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches rig by name" @@ -13110,6 +18883,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13121,7 +18897,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name patches rigs" @@ -13129,6 +18910,17 @@ "put": { "operationId": "put-v0-city-by-city-name-patches-rigs", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13161,7 +18953,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13171,7 +18968,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Put v0 city by city name patches rigs" @@ -13223,7 +19025,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13233,7 +19040,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name provider readiness" @@ -13243,6 +19055,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-provider-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13275,7 +19098,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13285,7 +19113,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name provider by name" @@ -13334,6 +19167,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13345,7 +19181,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name provider by name" @@ -13353,6 +19194,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-provider-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13395,7 +19247,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13405,7 +19262,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name provider by name" @@ -13446,6 +19308,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13457,7 +19322,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name providers" @@ -13465,6 +19335,17 @@ "post": { "operationId": "create-provider", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13497,7 +19378,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13507,7 +19393,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a provider" @@ -13548,6 +19439,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13559,7 +19453,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name providers public" @@ -13611,7 +19510,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13621,7 +19525,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name readiness" @@ -13631,6 +19540,17 @@ "delete": { "operationId": "delete-v0-city-by-city-name-rig-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13663,7 +19583,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13673,7 +19598,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name rig by name" @@ -13732,6 +19662,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13743,7 +19676,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name rig by name" @@ -13751,6 +19689,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-rig-by-name", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13793,7 +19742,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13803,7 +19757,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name rig by name" @@ -13813,6 +19772,17 @@ "post": { "operationId": "post-v0-city-by-city-name-rig-by-name-by-action", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13855,7 +19825,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13865,7 +19840,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name rig by name by action" @@ -13936,6 +19916,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -13947,7 +19930,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name rigs" @@ -13955,6 +19943,17 @@ "post": { "operationId": "create-rig", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -13987,7 +19986,12 @@ } } }, - "description": "Created" + "description": "Created", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -13997,7 +20001,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a rig" @@ -14048,6 +20057,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14059,7 +20071,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name service by name" @@ -14069,6 +20086,17 @@ "post": { "operationId": "post-v0-city-by-city-name-service-by-name-restart", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14101,7 +20129,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14111,7 +20144,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name service by name restart" @@ -14152,6 +20190,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14163,7 +20204,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name services" @@ -14224,6 +20270,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14235,7 +20284,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID" @@ -14243,6 +20297,17 @@ "patch": { "operationId": "patch-v0-city-by-city-name-session-by-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14294,6 +20359,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14305,7 +20373,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Patch v0 city by city name session by ID" @@ -14356,6 +20429,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14367,7 +20443,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID agents" @@ -14428,6 +20509,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14439,7 +20523,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID agents by agent ID" @@ -14449,6 +20538,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-close", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14491,7 +20591,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14501,7 +20606,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID close" @@ -14511,6 +20621,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-kill", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14543,7 +20664,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14553,7 +20679,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID kill" @@ -14563,6 +20694,17 @@ "post": { "operationId": "send-session-message", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14605,7 +20747,12 @@ } } }, - "description": "Accepted" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14615,7 +20762,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Send a message to a session" @@ -14666,6 +20818,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14677,7 +20832,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID pending" @@ -14687,6 +20847,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-rename", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14738,6 +20909,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -14749,7 +20923,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID rename" @@ -14759,6 +20938,17 @@ "post": { "operationId": "respond-session", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14801,7 +20991,12 @@ } } }, - "description": "Accepted" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14811,7 +21006,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Respond to a pending interaction" @@ -14821,6 +21021,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-stop", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -14853,7 +21064,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -14863,7 +21079,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID stop" @@ -15051,7 +21272,26 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "GC-Session-State": { + "description": "Session state at the time streaming began (e.g. active, closed).", + "schema": { + "description": "Session state at the time streaming began (e.g. active, closed).", + "type": "string" + } + }, + "GC-Session-Status": { + "description": "Runtime status at the time streaming began. Emitted as \"stopped\" when the session's underlying process is not running.", + "schema": { + "description": "Runtime status at the time streaming began. Emitted as \"stopped\" when the session's underlying process is not running.", + "type": "string" + } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15061,7 +21301,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream session output in real time" @@ -15071,6 +21316,17 @@ "post": { "operationId": "submit-session", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15113,7 +21369,12 @@ } } }, - "description": "Accepted" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15123,7 +21384,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Submit a message to a session" @@ -15133,6 +21399,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-suspend", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15165,7 +21442,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15175,7 +21457,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID suspend" @@ -15256,6 +21543,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -15267,7 +21557,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name session by ID transcript" @@ -15277,6 +21572,17 @@ "post": { "operationId": "post-v0-city-by-city-name-session-by-id-wake", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15309,7 +21615,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15319,7 +21630,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name session by ID wake" @@ -15412,6 +21728,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -15423,7 +21742,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name sessions" @@ -15431,6 +21755,17 @@ "post": { "operationId": "create-session", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15463,7 +21798,12 @@ } } }, - "description": "Accepted" + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15473,7 +21813,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Create a session" @@ -15483,6 +21828,17 @@ "post": { "operationId": "post-v0-city-by-city-name-sling", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15515,7 +21871,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15525,7 +21886,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Post v0 city by city name sling" @@ -15586,6 +21952,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -15597,16 +21966,93 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name status" } }, + "/v0/city/{cityName}/unregister": { + "post": { + "operationId": "post-v0-city-by-city-name-unregister", + "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, + { + "description": "Supervisor-registered city name.", + "in": "path", + "name": "cityName", + "required": true, + "schema": { + "description": "Supervisor-registered city name.", + "type": "string" + } + } + ], + "responses": { + "202": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CityUnregisterResponse" + } + } + }, + "description": "Accepted", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } + } + }, + "summary": "Post v0 city by city name unregister" + } + }, "/v0/city/{cityName}/workflow/{workflow_id}": { "delete": { "operationId": "delete-v0-city-by-city-name-workflow-by-workflow-id", "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, { "description": "City name.", "in": "path", @@ -15669,7 +22115,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15679,7 +22130,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Delete v0 city by city name workflow by workflow ID" @@ -15748,6 +22204,9 @@ "minimum": 0, "type": "integer" } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" } } }, @@ -15759,7 +22218,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 city by city name workflow by workflow ID" @@ -15821,7 +22285,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15831,7 +22300,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 events" @@ -15898,7 +22372,7 @@ { "properties": { "data": { - "$ref": "#/components/schemas/TaggedEventStreamEnvelope" + "$ref": "#/components/schemas/TypedTaggedEventStreamEnvelope" }, "event": { "const": "tagged_event", @@ -15928,7 +22402,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15938,7 +22417,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Stream tagged events from all running cities." @@ -15978,7 +22462,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -15988,7 +22477,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 provider readiness" @@ -16028,7 +22522,12 @@ } } }, - "description": "OK" + "description": "OK", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } }, "default": { "content": { @@ -16038,7 +22537,12 @@ } } }, - "description": "Error" + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } } }, "summary": "Get v0 readiness" diff --git a/internal/api/openapi_problem_types.go b/internal/api/openapi_problem_types.go new file mode 100644 index 0000000000..d301a85e51 --- /dev/null +++ b/internal/api/openapi_problem_types.go @@ -0,0 +1,45 @@ +package api + +import "github.com/danielgtaylor/huma/v2" + +const ( + slingMissingBeadProblemType = "urn:gascity:error:sling-missing-bead" + slingCrossRigProblemType = "urn:gascity:error:sling-cross-rig" +) + +var documentedProblemTypes = []string{ + slingMissingBeadProblemType, + slingCrossRigProblemType, +} + +func documentProblemTypes(oapi *huma.OpenAPI) { + if oapi == nil || oapi.Components == nil || oapi.Components.Schemas == nil { + return + } + errorModel := oapi.Components.Schemas.Map()["ErrorModel"] + if errorModel == nil || errorModel.Properties == nil { + return + } + typeSchema := errorModel.Properties["type"] + if typeSchema == nil { + return + } + for _, problemType := range documentedProblemTypes { + if !hasProblemTypeExample(typeSchema.Examples, problemType) { + typeSchema.Examples = append(typeSchema.Examples, problemType) + } + } + if typeSchema.Extensions == nil { + typeSchema.Extensions = map[string]any{} + } + typeSchema.Extensions["x-gascity-problem-types"] = append([]string(nil), documentedProblemTypes...) +} + +func hasProblemTypeExample(examples []any, problemType string) bool { + for _, example := range examples { + if s, ok := example.(string); ok && s == problemType { + return true + } + } + return false +} diff --git a/internal/api/openapi_sync_test.go b/internal/api/openapi_sync_test.go index 724018c635..97fd3955c6 100644 --- a/internal/api/openapi_sync_test.go +++ b/internal/api/openapi_sync_test.go @@ -23,7 +23,7 @@ import ( // yields the authoritative contract for every HTTP endpoint the control // plane exposes. func TestOpenAPISpecInSync(t *testing.T) { - sm := api.NewSupervisorMux(emptyTestResolver{}, false, "", time.Time{}) + sm := api.NewSupervisorMux(emptyTestResolver{}, nil, false, "", time.Time{}) req := httptest.NewRequest(http.MethodGet, "/openapi.json", nil) rec := httptest.NewRecorder() sm.ServeHTTP(rec, req) @@ -133,7 +133,7 @@ func TestEventsSchemaPublished(t *testing.T) { } func TestOrderResponseSchemaKeepsMigrationFieldsOptional(t *testing.T) { - sm := api.NewSupervisorMux(emptyTestResolver{}, false, "", time.Time{}) + sm := api.NewSupervisorMux(emptyTestResolver{}, nil, false, "", time.Time{}) req := httptest.NewRequest(http.MethodGet, "/openapi.json", nil) rec := httptest.NewRecorder() sm.ServeHTTP(rec, req) diff --git a/internal/api/server.go b/internal/api/server.go index 55cdf92812..0ba278792a 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -258,7 +258,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - sm := NewSupervisorMux(&singleStateResolver{state: s.state}, s.readOnly, "test", time.Now()) + sm := NewSupervisorMux(&singleStateResolver{state: s.state}, nil, s.readOnly, "test", time.Now()) sm.cacheMu.Lock() sm.cache[s.state.CityName()] = cachedCityServer{state: s.state, srv: s} sm.cacheMu.Unlock() diff --git a/internal/api/session_create_agent.go b/internal/api/session_create_agent.go new file mode 100644 index 0000000000..8712afd8b5 --- /dev/null +++ b/internal/api/session_create_agent.go @@ -0,0 +1,47 @@ +package api + +import ( + "fmt" + "strings" + + "github.com/gastownhall/gascity/internal/config" + workdirutil "github.com/gastownhall/gascity/internal/workdir" +) + +type agentCreateContext struct { + Agent config.Agent + Alias string + ExplicitName string + Identity string + WorkDir string +} + +func (s *Server) resolveAgentCreateContext(template, alias string) (agentCreateContext, error) { + cfg := s.state.Config() + if cfg == nil { + return agentCreateContext{}, fmt.Errorf("no city config loaded") + } + agentCfg, ok := resolveSessionTemplateAgent(cfg, template) + if !ok { + return agentCreateContext{}, fmt.Errorf("resolved agent template disappeared: %s", template) + } + if alias != "" && agentCfg.SupportsMultipleSessions() { + alias = workdirutil.SessionQualifiedName(s.state.CityPath(), agentCfg, cfg.Rigs, alias, "") + } + explicitName, err := sessionExplicitNameForCreate(agentCfg, alias) + if err != nil { + return agentCreateContext{}, err + } + identity := workdirutil.SessionQualifiedName(s.state.CityPath(), agentCfg, cfg.Rigs, alias, explicitName) + workDir, err := s.resolveSessionWorkDir(agentCfg, identity) + if err != nil { + return agentCreateContext{}, err + } + return agentCreateContext{ + Agent: agentCfg, + Alias: strings.TrimSpace(alias), + ExplicitName: explicitName, + Identity: identity, + WorkDir: workDir, + }, nil +} diff --git a/internal/api/session_manager.go b/internal/api/session_manager.go index c7aa802312..afea441a87 100644 --- a/internal/api/session_manager.go +++ b/internal/api/session_manager.go @@ -1,7 +1,10 @@ package api import ( + "strings" + "github.com/gastownhall/gascity/internal/beads" + "github.com/gastownhall/gascity/internal/config" "github.com/gastownhall/gascity/internal/session" ) @@ -10,11 +13,52 @@ func (s *Server) sessionManager(store beads.Store) *session.Manager { if cfg == nil { return session.NewManagerWithCityPath(store, s.state.SessionProvider(), s.state.CityPath()) } - return session.NewManagerWithTransportResolverAndCityPath(store, s.state.SessionProvider(), s.state.CityPath(), func(template string) string { - agentCfg, ok := resolveSessionTemplateAgent(cfg, template) - if !ok { - return "" + return session.NewManagerWithTransportPolicyResolverAndCityPath( + store, + s.state.SessionProvider(), + s.state.CityPath(), + func(template, provider string) (string, bool) { + return configuredSessionTransportResolution(cfg, template, provider) + }, + ) +} + +func configuredSessionTransport(cfg *config.City, template, provider string) string { + transport, _ := configuredSessionTransportResolution(cfg, template, provider) + return transport +} + +func configuredSessionTransportResolution(cfg *config.City, template, provider string) (string, bool) { + if cfg == nil { + return "", false + } + if agentCfg, ok := resolveSessionTemplateAgent(cfg, template); ok { + resolved, err := config.ResolveProvider( + &agentCfg, + &cfg.Workspace, + cfg.Providers, + func(name string) (string, error) { return name, nil }, + ) + if err != nil { + return strings.TrimSpace(agentCfg.Session), false } - return agentCfg.Session - }) + return config.ResolveSessionCreateTransport(agentCfg.Session, resolved), false + } + provider = strings.TrimSpace(provider) + if provider == "" { + provider = strings.TrimSpace(template) + } + if provider == "" { + return "", false + } + resolved, err := config.ResolveProvider( + &config.Agent{Provider: provider}, + &cfg.Workspace, + cfg.Providers, + func(name string) (string, error) { return name, nil }, + ) + if err != nil { + return "", false + } + return strings.TrimSpace(resolved.ProviderSessionCreateTransport()), false } diff --git a/internal/api/session_resolution.go b/internal/api/session_resolution.go index da195f85b7..4540e6f639 100644 --- a/internal/api/session_resolution.go +++ b/internal/api/session_resolution.go @@ -275,7 +275,11 @@ func (s *Server) materializeNamedSessionWithContext(ctx context.Context, store b return "", err } - resolved, _, transport, qualifiedTemplate, err := s.resolveSessionTemplate(spec.Agent.QualifiedName()) + resolved, _, transport, qualifiedTemplate, err := s.resolveSessionTemplateForCreate(spec.Agent.QualifiedName()) + if err != nil { + return "", err + } + transport, err = validateSessionTransport(resolved, transport, s.state.SessionProvider()) if err != nil { return "", err } @@ -285,7 +289,7 @@ func (s *Server) materializeNamedSessionWithContext(ctx context.Context, store b if err != nil { return "", err } - launchCommand, err := config.BuildProviderLaunchCommand(s.state.CityPath(), resolved, nil) + launchCommand, err := config.BuildProviderLaunchCommand(s.state.CityPath(), resolved, nil, transport) if err != nil { return "", err } @@ -308,7 +312,17 @@ func (s *Server) materializeNamedSessionWithContext(ctx context.Context, store b if resolved.BuiltinAncestor != "" && resolved.BuiltinAncestor != resolved.Name { extraMeta["builtin_ancestor"] = resolved.BuiltinAncestor } - hints := sessionCreateHints(resolved) + mcpServers, err := s.sessionMCPServers(qualifiedTemplate, resolved.Name, spec.Identity, workDir, transport, "") + if err != nil { + return "", err + } + if transport == "acp" { + extraMeta, err = session.WithStoredMCPMetadata(extraMeta, spec.Identity, mcpServers) + if err != nil { + return "", err + } + } + hints := sessionCreateHints(resolved, mcpServers) var info session.Info err = session.WithCitySessionIdentifierLocks(s.state.CityPath(), []string{spec.Identity, spec.SessionName}, func() error { if err := session.EnsureAliasAvailableWithConfigForOwner(store, s.state.Config(), spec.Identity, "", spec.Identity); err != nil { diff --git a/internal/api/session_resolved_config.go b/internal/api/session_resolved_config.go index bd7621aee2..8e746f111e 100644 --- a/internal/api/session_resolved_config.go +++ b/internal/api/session_resolved_config.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/gastownhall/gascity/internal/config" + "github.com/gastownhall/gascity/internal/runtime" "github.com/gastownhall/gascity/internal/session" "github.com/gastownhall/gascity/internal/worker" ) @@ -13,10 +14,28 @@ func resolvedSessionConfigForProvider( metadata map[string]string, resolved *config.ResolvedProvider, command, workDir string, + mcpServers []runtime.MCPServerConfig, ) (worker.ResolvedSessionConfig, error) { if resolved == nil { return worker.ResolvedSessionConfig{}, fmt.Errorf("%w: resolved provider is required", worker.ErrHandleConfig) } + if transport == "acp" { + var err error + metadata, err = session.WithStoredMCPMetadata( + metadata, + firstNonEmptyString(metadata[session.MCPIdentityMetadataKey], metadata["agent_name"]), + mcpServers, + ) + if err != nil { + return worker.ResolvedSessionConfig{}, err + } + } + // Use the ACP-specific command when the session uses ACP transport, + // falling back to the default command for tmux sessions. + resolvedCommand := resolved.CommandString() + if transport == "acp" { + resolvedCommand = resolved.ACPCommandString() + } return worker.NormalizeResolvedSessionConfig(worker.ResolvedSessionConfig{ Alias: alias, ExplicitName: explicitName, @@ -25,7 +44,7 @@ func resolvedSessionConfigForProvider( Transport: transport, Metadata: metadata, Runtime: worker.ResolvedRuntime{ - Command: firstNonEmptyString(command, resolved.CommandString(), resolved.Name), + Command: firstNonEmptyString(command, resolvedCommand, resolved.Name), WorkDir: workDir, Provider: resolved.Name, SessionEnv: resolved.Env, @@ -35,7 +54,7 @@ func resolvedSessionConfigForProvider( ResumeCommand: resolved.ResumeCommand, SessionIDFlag: resolved.SessionIDFlag, }, - Hints: sessionCreateHints(resolved), + Hints: sessionCreateHints(resolved, mcpServers), }, }) } diff --git a/internal/api/session_resolved_config_test.go b/internal/api/session_resolved_config_test.go index f1a84578d2..106e5d24f6 100644 --- a/internal/api/session_resolved_config_test.go +++ b/internal/api/session_resolved_config_test.go @@ -4,11 +4,21 @@ import ( "testing" "github.com/gastownhall/gascity/internal/config" + "github.com/gastownhall/gascity/internal/runtime" + "github.com/gastownhall/gascity/internal/session" ) func TestResolvedSessionConfigForProviderBuildsNormalizedConfig(t *testing.T) { - metadata := map[string]string{"session_origin": "named"} + metadata := map[string]string{ + "session_origin": "named", + "agent_name": "myrig/worker-adhoc-123", + } env := map[string]string{"API_TOKEN": "present"} + mcpServers := []runtime.MCPServerConfig{{ + Name: "filesystem", + Command: "/bin/mcp", + Args: []string{"--stdio"}, + }} resolved := &config.ResolvedProvider{ Name: "stub", Command: "/bin/echo", @@ -33,6 +43,7 @@ func TestResolvedSessionConfigForProviderBuildsNormalizedConfig(t *testing.T) { resolved, "", "/tmp/workdir", + mcpServers, ) if err != nil { t.Fatalf("resolvedSessionConfigForProvider: %v", err) @@ -53,9 +64,21 @@ func TestResolvedSessionConfigForProviderBuildsNormalizedConfig(t *testing.T) { if got, want := cfg.Runtime.Hints.ReadyPromptPrefix, "stub-ready>"; got != want { t.Fatalf("Runtime.Hints.ReadyPromptPrefix = %q, want %q", got, want) } + if len(cfg.Runtime.Hints.MCPServers) != 1 { + t.Fatalf("Runtime.Hints.MCPServers len = %d, want 1", len(cfg.Runtime.Hints.MCPServers)) + } + if got, want := cfg.Runtime.Hints.MCPServers[0].Name, "filesystem"; got != want { + t.Fatalf("Runtime.Hints.MCPServers[0].Name = %q, want %q", got, want) + } if got, want := cfg.Runtime.Resume.SessionIDFlag, "--session-id"; got != want { t.Fatalf("Runtime.Resume.SessionIDFlag = %q, want %q", got, want) } + if got, want := cfg.Metadata[session.MCPIdentityMetadataKey], "myrig/worker-adhoc-123"; got != want { + t.Fatalf("Metadata[mcp_identity] = %q, want %q", got, want) + } + if got := cfg.Metadata[session.MCPServersSnapshotMetadataKey]; got == "" { + t.Fatal("Metadata[mcp_servers_snapshot] = empty, want persisted snapshot") + } metadata["session_origin"] = "mutated" env["API_TOKEN"] = "mutated" @@ -78,7 +101,38 @@ func TestResolvedSessionConfigForProviderRejectsNilProvider(t *testing.T) { nil, "", "/tmp/workdir", + nil, ); err == nil { t.Fatal("resolvedSessionConfigForProvider() error = nil, want error") } } + +func TestResolvedSessionConfigForProviderSkipsStoredMCPMetadataForTmuxTransport(t *testing.T) { + cfg, err := resolvedSessionConfigForProvider( + "worker", + "", + "myrig/worker", + "Worker", + "", + map[string]string{ + "session_origin": "manual", + "agent_name": "myrig/worker-adhoc-123", + }, + &config.ResolvedProvider{ + Name: "stub", + Command: "/bin/echo", + }, + "", + "/tmp/workdir", + nil, + ) + if err != nil { + t.Fatalf("resolvedSessionConfigForProvider: %v", err) + } + if got := cfg.Metadata[session.MCPIdentityMetadataKey]; got != "" { + t.Fatalf("Metadata[mcp_identity] = %q, want empty for tmux transport", got) + } + if got := cfg.Metadata[session.MCPServersSnapshotMetadataKey]; got != "" { + t.Fatalf("Metadata[mcp_servers_snapshot] = %q, want empty for tmux transport", got) + } +} diff --git a/internal/api/session_runtime.go b/internal/api/session_runtime.go index 3be2da5b9c..d13088c191 100644 --- a/internal/api/session_runtime.go +++ b/internal/api/session_runtime.go @@ -7,12 +7,15 @@ import ( "strings" "github.com/gastownhall/gascity/internal/config" + "github.com/gastownhall/gascity/internal/materialize" "github.com/gastownhall/gascity/internal/runtime" "github.com/gastownhall/gascity/internal/session" workdirutil "github.com/gastownhall/gascity/internal/workdir" "github.com/gastownhall/gascity/internal/worker" ) +var errAmbiguousLegacyACPTransport = errors.New("legacy session transport is ambiguous") + func (s *Server) sessionLogPaths() []string { if s.sessionLogSearchPaths != nil { return s.sessionLogSearchPaths @@ -24,16 +27,17 @@ func (s *Server) sessionLogPaths() []string { return worker.MergeSearchPaths(cfg.Daemon.ObservePaths) } -func sessionCreateHints(resolved *config.ResolvedProvider) runtime.Config { +func sessionCreateHints(resolved *config.ResolvedProvider, mcpServers []runtime.MCPServerConfig) runtime.Config { return runtime.Config{ ReadyPromptPrefix: resolved.ReadyPromptPrefix, ReadyDelayMs: resolved.ReadyDelayMs, ProcessNames: resolved.ProcessNames, EmitsPermissionWarning: resolved.EmitsPermissionWarning, + MCPServers: mcpServers, } } -func sessionResumeHints(resolved *config.ResolvedProvider, workDir string) runtime.Config { +func sessionResumeHints(resolved *config.ResolvedProvider, workDir string, mcpServers []runtime.MCPServerConfig) runtime.Config { return runtime.Config{ WorkDir: workDir, ReadyPromptPrefix: resolved.ReadyPromptPrefix, @@ -41,7 +45,101 @@ func sessionResumeHints(resolved *config.ResolvedProvider, workDir string) runti ProcessNames: resolved.ProcessNames, EmitsPermissionWarning: resolved.EmitsPermissionWarning, Env: resolved.Env, + MCPServers: mcpServers, + } +} + +func resumeSessionIdentity(info session.Info, metadata map[string]string) string { + if metadata != nil { + if identity := strings.TrimSpace(metadata[session.MCPIdentityMetadataKey]); identity != "" { + return identity + } + } + return firstNonEmptyString(info.AgentName, info.Alias, info.Template, info.Provider) +} + +func (s *Server) resumeSessionMCPServers(info session.Info, metadata map[string]string, resolved *config.ResolvedProvider, workDir, transport string) ([]runtime.MCPServerConfig, error) { + if resolved == nil { + return nil, nil } + mcpServers, err := s.sessionMCPServers( + info.Template, + firstNonEmptyString(info.Provider, resolved.Name), + resumeSessionIdentity(info, metadata), + workDir, + transport, + s.sessionKind(info.ID), + ) + if err == nil { + return mcpServers, nil + } + runtimeSnapshot, loadErr := session.LoadRuntimeMCPServersSnapshot(s.state.CityPath(), info.ID) + if loadErr != nil { + return nil, loadErr + } + if len(runtimeSnapshot) > 0 { + return runtimeSnapshot, nil + } + stored, decodeErr := session.DecodeMCPServersSnapshot(metadata[session.MCPServersSnapshotMetadataKey]) + if decodeErr != nil { + return nil, fmt.Errorf("decoding stored MCP snapshot: %w", decodeErr) + } + return session.SanitizeStoredMCPSnapshotForResume(stored), nil +} + +func (s *Server) providerSessionMCPServers(providerName, identity, workDir, transport string) ([]runtime.MCPServerConfig, error) { + cfg := s.state.Config() + if cfg == nil || strings.TrimSpace(workDir) == "" || strings.TrimSpace(transport) != "acp" { + return nil, nil + } + synthetic := &config.Agent{Provider: providerName} + catalog, err := materialize.EffectiveMCPForSession(cfg, s.state.CityPath(), synthetic, firstNonEmptyString(identity, providerName), workDir) + if err != nil { + return nil, fmt.Errorf("loading effective MCP: %w", err) + } + return materialize.RuntimeMCPServers(catalog.Servers), nil +} + +func (s *Server) sessionMCPServers(template, providerName, identity, workDir, transport, sessionKind string) ([]runtime.MCPServerConfig, error) { + cfg := s.state.Config() + if cfg == nil || strings.TrimSpace(workDir) == "" || strings.TrimSpace(transport) != "acp" { + return nil, nil + } + if sessionKind != "provider" { + if agentCfg, ok := resolveSessionTemplateAgent(cfg, template); ok { + catalog, err := materialize.EffectiveMCPForSession( + cfg, + s.state.CityPath(), + &agentCfg, + firstNonEmptyString(identity, template), + workDir, + ) + if err != nil { + return nil, fmt.Errorf("loading effective MCP: %w", err) + } + return materialize.RuntimeMCPServers(catalog.Servers), nil + } + } + return s.providerSessionMCPServers(firstNonEmptyString(providerName, template), identity, workDir, transport) +} + +func (s *Server) sessionMetadata(sessionID string) map[string]string { + store := s.state.CityBeadStore() + if store == nil || strings.TrimSpace(sessionID) == "" { + return nil + } + bead, err := store.Get(sessionID) + if err != nil { + return nil + } + return bead.Metadata +} + +func providerSessionMCPIdentity(providerName, alias string) (string, error) { + if alias = strings.TrimSpace(alias); alias != "" { + return alias, nil + } + return session.GenerateAdhocIdentity(providerName) } func sessionExplicitNameForCreate(agentCfg config.Agent, alias string) (string, error) { @@ -77,7 +175,7 @@ func (s *Server) resolveSessionWorkDir(agentCfg config.Agent, qualifiedName stri // agent name that matches exactly one configured agent. Keeps the // two-phase lookup out of the handler. func (s *Server) resolveSessionTemplateWithBareNameFallback(name string) (*config.ResolvedProvider, string, string, string, error) { - resolved, workDir, transport, template, err := s.resolveSessionTemplate(name) + resolved, workDir, transport, template, err := s.resolveSessionTemplateForCreate(name) if err == nil { return resolved, workDir, transport, template, nil } @@ -88,9 +186,30 @@ func (s *Server) resolveSessionTemplateWithBareNameFallback(name string) (*confi if !ok { return nil, "", "", "", err } - return s.resolveSessionTemplate(agentCfg.QualifiedName()) + return s.resolveSessionTemplateForCreate(agentCfg.QualifiedName()) +} + +func (s *Server) resolveSessionTemplateForCreate(template string) (*config.ResolvedProvider, string, string, string, error) { + cfg := s.state.Config() + if cfg == nil { + return nil, "", "", "", errors.New("no city config loaded") + } + agentCfg, ok := resolveSessionTemplateAgent(cfg, template) + if !ok { + return nil, "", "", "", errSessionTemplateNotFound + } + resolved, err := config.ResolveProvider(&agentCfg, &cfg.Workspace, cfg.Providers, exec.LookPath) + if err != nil { + return nil, "", "", "", err + } + workDir, err := s.resolveSessionWorkDir(agentCfg, agentCfg.QualifiedName()) + if err != nil { + return nil, "", "", "", err + } + return resolved, workDir, config.ResolveSessionCreateTransport(agentCfg.Session, resolved), agentCfg.QualifiedName(), nil } +//nolint:unparam // kept as a focused test helper even though current call sites use one template shape. func (s *Server) resolveSessionTemplate(template string) (*config.ResolvedProvider, string, string, string, error) { cfg := s.state.Config() if cfg == nil { @@ -108,37 +227,76 @@ func (s *Server) resolveSessionTemplate(template string) (*config.ResolvedProvid if err != nil { return nil, "", "", "", err } - return resolved, workDir, agentCfg.Session, agentCfg.QualifiedName(), nil + return resolved, workDir, config.ResolveSessionCreateTransport(agentCfg.Session, resolved), agentCfg.QualifiedName(), nil } -func (s *Server) buildSessionResume(info session.Info) (string, runtime.Config) { +func (s *Server) buildSessionResume(info session.Info) (string, runtime.Config, error) { cmd := session.BuildResumeCommand(info) - resolved, workDir := s.resolveSessionRuntime(info) + metadata := s.sessionMetadata(info.ID) + resolved, workDir, transport, ambiguous := s.resolveSessionRuntimeWithMetadata(info, metadata) if resolved == nil { - return cmd, runtime.Config{WorkDir: info.WorkDir} + return cmd, runtime.Config{WorkDir: info.WorkDir}, nil + } + if ambiguous { + return "", runtime.Config{}, fmt.Errorf("%w: recreate the stopped session or resume it while ACP metadata can still be persisted", errAmbiguousLegacyACPTransport) + } + mcpServers, err := s.resumeSessionMCPServers(info, metadata, resolved, firstNonEmptyString(workDir, info.WorkDir), transport) + if err != nil { + return "", runtime.Config{}, err } resolvedInfo := info - if command, err := s.resolvedSessionRuntimeCommand(resolved, info.Command); err == nil { + if command, err := s.resolvedSessionRuntimeCommand(resolved, transport, info.Command, metadata); err == nil { resolvedInfo.Command = command } else { - resolvedInfo.Command = firstNonEmptyString(info.Command, resolved.CommandString(), resolved.Name) + resolvedInfo.Command = fallbackSessionRuntimeCommand(resolved, transport, info.Command, info.Provider) } resolvedInfo.Provider = resolved.Name + resolvedInfo.Transport = transport resolvedInfo.ResumeFlag = resolved.ResumeFlag resolvedInfo.ResumeStyle = resolved.ResumeStyle resolvedInfo.ResumeCommand = resolved.ResumeCommand - return session.BuildResumeCommand(resolvedInfo), sessionResumeHints(resolved, workDir) + return session.BuildResumeCommand(resolvedInfo), sessionResumeHints(resolved, workDir, mcpServers), nil } -func (s *Server) resolvedSessionRuntimeCommand(resolved *config.ResolvedProvider, storedCommand string) (string, error) { - if command := strings.TrimSpace(storedCommand); shouldPreserveStoredRuntimeCommand(command, resolved.CommandString()) { - return command, nil +func (s *Server) resolvedSessionRuntimeCommand(resolved *config.ResolvedProvider, transport, storedCommand string, metadata map[string]string) (string, error) { + configuredCommand := configuredSessionRuntimeCommand(resolved, transport) + if configuredCommand == "" { + if command := strings.TrimSpace(storedCommand); command != "" { + return command, nil + } + return "", fmt.Errorf("resolved provider %q has no launch command", resolved.Name) } - launchCommand, err := config.BuildProviderLaunchCommand(s.state.CityPath(), resolved, nil) + optionOverrides, err := session.ParseTemplateOverrides(metadata) + if err != nil { + return "", fmt.Errorf("parsing template overrides: %w", err) + } + launchCommand, err := config.BuildProviderLaunchCommand(s.state.CityPath(), resolved, optionOverrides, transport) if err != nil { return "", fmt.Errorf("building provider launch command: %w", err) } - return firstNonEmptyString(launchCommand.Command, resolved.CommandString(), resolved.Name), nil + desiredCommand := firstNonEmptyString(launchCommand.Command, configuredCommand, resolved.Name) + if command := strings.TrimSpace(storedCommand); shouldPreserveStoredRuntimeCommandForTransport(command, desiredCommand, transport, optionOverrides) { + return command, nil + } + return desiredCommand, nil +} + +func configuredSessionRuntimeCommand(resolved *config.ResolvedProvider, transport string) string { + if resolved == nil { + return "" + } + if transport == "acp" && (strings.TrimSpace(resolved.ACPCommand) != "" || resolved.ACPArgs != nil) { + return strings.TrimSpace(resolved.ACPCommandString()) + } + if strings.TrimSpace(resolved.Command) != "" { + return strings.TrimSpace(resolved.CommandString()) + } + return "" +} + +func fallbackSessionRuntimeCommand(resolved *config.ResolvedProvider, transport, storedCommand, fallbackProvider string) string { + resolvedCommand := configuredSessionRuntimeCommand(resolved, transport) + return firstNonEmptyString(storedCommand, resolvedCommand, fallbackProvider, resolved.Name) } func shouldPreserveStoredRuntimeCommand(storedCommand, resolvedCommand string) bool { @@ -150,24 +308,70 @@ func shouldPreserveStoredRuntimeCommand(storedCommand, resolvedCommand string) b if resolvedCommand == "" { return true } - return storedCommand == resolvedCommand || strings.HasPrefix(storedCommand, resolvedCommand+" ") + // A bare stored command (just the provider binary) lacks schema + // defaults like --dangerously-skip-permissions and the --settings + // path. Rebuild from the current config instead of preserving it. + // See #799: pool-agent sessions resumed through the control- + // dispatcher path wedged on interactive permission prompts because + // the bare stored command was preserved without re-injecting flags. + if storedCommand == resolvedCommand { + return false + } + return strings.HasPrefix(storedCommand, resolvedCommand+" ") +} + +func shouldPreserveStoredRuntimeCommandForTransport(storedCommand, resolvedCommand, _ string, optionOverrides map[string]string) bool { + if shouldPreserveStoredRuntimeCommand(storedCommand, resolvedCommand) { + return true + } + if len(optionOverrides) == 0 && storedCommandHasSettingsArg(storedCommand) && sameRuntimeCommandExecutable(storedCommand, resolvedCommand) { + return true + } + return false } -func (s *Server) resolveWorkerSessionRuntime(info session.Info, _ string) (*worker.ResolvedRuntime, error) { - resolved, workDir := s.resolveSessionRuntime(info) +func sameRuntimeCommandExecutable(storedCommand, resolvedCommand string) bool { + storedFields := strings.Fields(strings.TrimSpace(storedCommand)) + resolvedFields := strings.Fields(strings.TrimSpace(resolvedCommand)) + if len(storedFields) == 0 || len(resolvedFields) == 0 { + return false + } + return storedFields[0] == resolvedFields[0] +} + +func storedCommandHasSettingsArg(command string) bool { + return strings.Contains(" "+strings.TrimSpace(command)+" ", " --settings ") +} + +func (s *Server) resolveWorkerSessionRuntime(info session.Info) (*worker.ResolvedRuntime, error) { + return s.resolveWorkerSessionRuntimeWithMetadata(info, "", nil) +} + +func (s *Server) resolveWorkerSessionRuntimeWithMetadata(info session.Info, _ string, metadata map[string]string) (*worker.ResolvedRuntime, error) { + if metadata == nil { + metadata = s.sessionMetadata(info.ID) + } + resolved, workDir, transport, ambiguous := s.resolveSessionRuntimeWithMetadata(info, metadata) if resolved == nil { return nil, nil } - command, err := s.resolvedSessionRuntimeCommand(resolved, info.Command) + if ambiguous { + return nil, fmt.Errorf("%w: recreate the stopped session or resume it while ACP metadata can still be persisted", errAmbiguousLegacyACPTransport) + } + mcpServers, err := s.resumeSessionMCPServers(info, metadata, resolved, firstNonEmptyString(workDir, info.WorkDir), transport) if err != nil { return nil, err } + command, err := s.resolvedSessionRuntimeCommand(resolved, transport, info.Command, metadata) + if err != nil { + command = fallbackSessionRuntimeCommand(resolved, transport, info.Command, info.Provider) + } runtimeCfg, err := worker.NormalizeResolvedRuntime(worker.ResolvedRuntime{ Command: command, WorkDir: firstNonEmptyString(info.WorkDir, workDir), Provider: firstNonEmptyString(info.Provider, resolved.Name), SessionEnv: resolved.Env, - Hints: sessionResumeHints(resolved, firstNonEmptyString(workDir, info.WorkDir)), + Hints: sessionResumeHints(resolved, firstNonEmptyString(workDir, info.WorkDir), mcpServers), Resume: session.ProviderResume{ ResumeFlag: firstNonEmptyString(resolved.ResumeFlag, info.ResumeFlag), ResumeStyle: firstNonEmptyString(resolved.ResumeStyle, info.ResumeStyle), @@ -181,27 +385,161 @@ func (s *Server) resolveWorkerSessionRuntime(info session.Info, _ string) (*work return &runtimeCfg, nil } -func (s *Server) resolveSessionRuntime(info session.Info) (*config.ResolvedProvider, string) { - kind := s.sessionKind(info.ID) - if kind != "provider" { - resolved, workDir, _, _, err := s.resolveSessionTemplate(info.Template) - if err == nil { - if info.WorkDir != "" { - workDir = info.WorkDir - } - return resolved, workDir +func storedSessionProvesACPTransport(resolved *config.ResolvedProvider, configuredTransport, storedCommand string, metadata map[string]string) bool { + if metadata != nil { + if strings.TrimSpace(metadata[session.MCPIdentityMetadataKey]) != "" || + strings.TrimSpace(metadata[session.MCPServersSnapshotMetadataKey]) != "" { + return true + } + if strings.TrimSpace(configuredTransport) == "acp" && legacyResumeMetadataProvesACPTransport(metadata) { + return true } } + if resolved == nil { + return false + } + acpCommand := strings.TrimSpace(resolved.ACPCommandString()) + defaultCommand := strings.TrimSpace(resolved.CommandString()) + if acpCommand == "" || acpCommand == defaultCommand { + return false + } + return shouldPreserveStoredRuntimeCommand(storedCommand, acpCommand) +} + +func legacyResumeMetadataProvesACPTransport(metadata map[string]string) bool { + if metadata == nil { + return false + } + return strings.TrimSpace(metadata["resume_command"]) != "" || + strings.TrimSpace(metadata["resume_flag"]) != "" || + strings.TrimSpace(metadata["session_key"]) != "" +} + +func legacyACPTransportAmbiguous(resolved *config.ResolvedProvider, configuredTransport, storedCommand string, metadata map[string]string) bool { + if strings.TrimSpace(configuredTransport) != "acp" || resolved == nil { + return false + } + if storedSessionProvesACPTransport(resolved, configuredTransport, storedCommand, metadata) { + return false + } + acpCommand := strings.TrimSpace(resolved.ACPCommandString()) + defaultCommand := strings.TrimSpace(resolved.CommandString()) + if acpCommand == "" || acpCommand != defaultCommand { + return false + } + storedCommand = strings.TrimSpace(storedCommand) + return storedCommand == "" || sameRuntimeCommandExecutable(storedCommand, defaultCommand) +} - resolved, err := s.resolveBareProvider(info.Template) +func (s *Server) startedConfigHashProvesACPTransport( + info session.Info, + metadata map[string]string, + resolved *config.ResolvedProvider, + workDir, + configuredTransport, + sessionKind string, +) bool { + if strings.TrimSpace(configuredTransport) != "acp" || resolved == nil || metadata == nil { + return false + } + startedHash := strings.TrimSpace(metadata["started_config_hash"]) + if startedHash == "" { + return false + } + acpCommand, err := s.resolvedSessionRuntimeCommand(resolved, "acp", info.Command, metadata) if err != nil { - return nil, "" + acpCommand = fallbackSessionRuntimeCommand(resolved, "acp", info.Command, info.Provider) } - workDir := info.WorkDir - if workDir == "" { - workDir = s.state.CityPath() + defaultCommand, err := s.resolvedSessionRuntimeCommand(resolved, "", info.Command, metadata) + if err != nil { + defaultCommand = fallbackSessionRuntimeCommand(resolved, "", info.Command, info.Provider) + } + mcpServers, err := s.sessionMCPServers( + info.Template, + firstNonEmptyString(info.Provider, resolved.Name), + resumeSessionIdentity(info, metadata), + firstNonEmptyString(workDir, info.WorkDir), + "acp", + sessionKind, + ) + if err != nil { + return false + } + acpHash := runtime.CoreFingerprint(runtime.Config{ + Command: acpCommand, + Env: resolved.Env, + MCPServers: mcpServers, + }) + defaultHash := runtime.CoreFingerprint(runtime.Config{ + Command: defaultCommand, + Env: resolved.Env, + }) + if acpHash == defaultHash { + return false + } + return startedHash == acpHash +} + +func resolvedSessionTransport(info session.Info, resolved *config.ResolvedProvider, configuredTransport string, metadata map[string]string, allowConfiguredTransportFallback bool) string { + if transport := strings.TrimSpace(info.Transport); transport != "" { + return transport + } + if strings.TrimSpace(info.Provider) == "acp" { + return "acp" + } + if storedSessionProvesACPTransport(resolved, configuredTransport, info.Command, metadata) { + return "acp" + } + if strings.TrimSpace(info.Command) == "" { + return strings.TrimSpace(configuredTransport) + } + if allowConfiguredTransportFallback { + return strings.TrimSpace(configuredTransport) + } + return "" +} + +func (s *Server) resolveSessionRuntimeWithMetadata(info session.Info, metadata map[string]string) (*config.ResolvedProvider, string, string, bool) { + kind := s.sessionKind(info.ID) + cfg := s.state.Config() + var ( + resolved *config.ResolvedProvider + workDir string + configuredTransport string + ) + if kind != "provider" && cfg != nil { + if agentCfg, ok := resolveSessionTemplateAgent(cfg, info.Template); ok { + candidate, err := config.ResolveProvider(&agentCfg, &cfg.Workspace, cfg.Providers, exec.LookPath) + if err == nil { + candidateWorkDir, workDirErr := s.resolveSessionWorkDir(agentCfg, agentCfg.QualifiedName()) + if workDirErr == nil { + resolved = candidate + workDir = candidateWorkDir + if info.WorkDir != "" { + workDir = info.WorkDir + } + configuredTransport = config.ResolveSessionCreateTransport(agentCfg.Session, resolved) + } + } + } + } + if resolved == nil { + candidate, err := s.resolveBareProvider(info.Template) + if err != nil { + return nil, "", "", false + } + resolved = candidate + workDir = info.WorkDir + if workDir == "" { + workDir = s.state.CityPath() + } + configuredTransport = resolved.ProviderSessionCreateTransport() + } + transport := resolvedSessionTransport(info, resolved, configuredTransport, metadata, false) + if transport == "" && s.startedConfigHashProvesACPTransport(info, metadata, resolved, workDir, configuredTransport, kind) { + transport = "acp" } - return resolved, workDir + return resolved, workDir, transport, transport == "" && legacyACPTransportAmbiguous(resolved, configuredTransport, info.Command, metadata) } // sessionKind reads the persisted mc_session_kind from bead metadata. diff --git a/internal/api/session_transport.go b/internal/api/session_transport.go new file mode 100644 index 0000000000..301b73e2f5 --- /dev/null +++ b/internal/api/session_transport.go @@ -0,0 +1,57 @@ +package api + +import ( + "fmt" + "strings" + + "github.com/gastownhall/gascity/internal/config" + "github.com/gastownhall/gascity/internal/runtime" +) + +type acpRoutingProvider interface { + RouteACP(name string) +} + +func validateSessionTransport(resolved *config.ResolvedProvider, transport string, sp runtime.Provider) (string, error) { + transport = strings.TrimSpace(transport) + if transport != "acp" { + return transport, nil + } + providerName := "" + if resolved != nil { + providerName = resolved.Name + if !resolved.SupportsACP { + if providerName == "" { + providerName = transport + } + return "", fmt.Errorf("provider %q does not support ACP transport", providerName) + } + } + if transportSupportsACP(sp) { + return transport, nil + } + if providerName == "" { + providerName = transport + } + return "", fmt.Errorf("provider %q requires ACP transport but the session provider cannot route ACP sessions", providerName) +} + +func providerSessionTransport(resolved *config.ResolvedProvider, sp runtime.Provider) (string, error) { + if resolved == nil { + return "", nil + } + return validateSessionTransport(resolved, resolved.ProviderSessionCreateTransport(), sp) +} + +func transportSupportsACP(sp runtime.Provider) bool { + if sp == nil { + return false + } + if provider, ok := sp.(runtime.TransportCapabilityProvider); ok { + return provider.SupportsTransport("acp") + } + if _, ok := sp.(acpRoutingProvider); ok { + return true + } + return false +} diff --git a/internal/api/session_transport_test.go b/internal/api/session_transport_test.go new file mode 100644 index 0000000000..0edce90b34 --- /dev/null +++ b/internal/api/session_transport_test.go @@ -0,0 +1,209 @@ +package api + +import ( + "testing" + + "github.com/gastownhall/gascity/internal/config" + "github.com/gastownhall/gascity/internal/runtime" + "github.com/gastownhall/gascity/internal/session" +) + +type createTransportCapableProvider struct { + *runtime.Fake +} + +func (p *createTransportCapableProvider) SupportsTransport(transport string) bool { + return transport == "acp" +} + +func TestProviderSessionTransportUsesExplicitACPConfigOnCustomProvider(t *testing.T) { + transport, err := providerSessionTransport(&config.ResolvedProvider{ + Name: "custom-acp", + SupportsACP: true, + ACPCommand: "/bin/echo", + }, &createTransportCapableProvider{Fake: runtime.NewFake()}) + if err != nil { + t.Fatalf("providerSessionTransport: %v", err) + } + if transport != "acp" { + t.Fatalf("providerSessionTransport() = %q, want %q", transport, "acp") + } +} + +func TestProviderSessionTransportSupportsACPAloneStaysDefault(t *testing.T) { + transport, err := providerSessionTransport(&config.ResolvedProvider{ + Name: "custom-acp", + SupportsACP: true, + }, &createTransportCapableProvider{Fake: runtime.NewFake()}) + if err != nil { + t.Fatalf("providerSessionTransport: %v", err) + } + if transport != "" { + t.Fatalf("providerSessionTransport() = %q, want empty transport", transport) + } +} + +func TestResolveSessionTemplateForCreateUsesProviderACPDefault(t *testing.T) { + fs := newSessionFakeState(t) + supportsACP := true + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{{ + Name: "worker", + Dir: "myrig", + Provider: "custom-acp", + }}, + Providers: map[string]config.ProviderSpec{ + "custom-acp": { + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + + srv := New(fs) + _, _, transport, _, err := srv.resolveSessionTemplateForCreate("myrig/worker") + if err != nil { + t.Fatalf("resolveSessionTemplateForCreate: %v", err) + } + if transport != "acp" { + t.Fatalf("transport = %q, want %q", transport, "acp") + } +} + +func TestResolveSessionTemplateUsesProviderACPDefaultForLegacyRuntimeTransport(t *testing.T) { + fs := newSessionFakeState(t) + supportsACP := true + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{{ + Name: "worker", + Dir: "myrig", + Provider: "custom-acp", + }}, + Providers: map[string]config.ProviderSpec{ + "custom-acp": { + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + + srv := New(fs) + _, _, transport, _, err := srv.resolveSessionTemplate("myrig/worker") + if err != nil { + t.Fatalf("resolveSessionTemplate: %v", err) + } + if transport != "acp" { + t.Fatalf("transport = %q, want %q", transport, "acp") + } +} + +func TestConfiguredSessionTransportUsesProviderACPDefaultForAgentTemplates(t *testing.T) { + supportsACP := true + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{{ + Name: "worker", + Dir: "myrig", + Provider: "custom-acp", + }}, + Providers: map[string]config.ProviderSpec{ + "custom-acp": { + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + + transport := configuredSessionTransport(cfg, "myrig/worker", "") + if transport != "acp" { + t.Fatalf("configuredSessionTransport() = %q, want %q", transport, "acp") + } +} + +func TestBuildSessionResumeDoesNotInferProviderACPDefaultForStoppedLegacyTemplateSession(t *testing.T) { + fs := newSessionFakeState(t) + supportsACP := true + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{{ + Name: "worker", + Dir: "myrig", + Provider: "custom-acp", + }}, + Providers: map[string]config.ProviderSpec{ + "custom-acp": { + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + }, + }, + } + + srv := New(fs) + cmd, _, err := srv.buildSessionResume(session.Info{ + ID: "gc-1", + Template: "myrig/worker", + Command: "/bin/echo", + WorkDir: "/tmp/workdir", + }) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + if cmd != "/bin/echo" { + t.Fatalf("resume command = %q, want %q", cmd, "/bin/echo") + } +} + +func TestResolvedSessionRuntimeCommandReplaysTemplateOverrides(t *testing.T) { + fs := newSessionFakeState(t) + srv := New(fs) + resolved := &config.ResolvedProvider{ + Name: "custom", + Command: "/bin/echo", + OptionsSchema: []config.ProviderOption{{ + Key: "effort", + Type: "select", + Choices: []config.OptionChoice{{ + Value: "high", + FlagArgs: []string{"--effort", "high"}, + }}, + }}, + } + + command, err := srv.resolvedSessionRuntimeCommand( + resolved, + "", + "/bin/echo", + map[string]string{"template_overrides": `{"effort":"high","initial_message":"hello"}`}, + ) + if err != nil { + t.Fatalf("resolvedSessionRuntimeCommand: %v", err) + } + if command != "/bin/echo --effort high" { + t.Fatalf("command = %q, want %q", command, "/bin/echo --effort high") + } +} + +func TestShouldPreserveStoredRuntimeCommandForTransportRejectsExecutableOnlyMatch(t *testing.T) { + if shouldPreserveStoredRuntimeCommandForTransport( + "claude", + "claude --settings /tmp/settings.json", + "", + nil, + ) { + t.Fatal("shouldPreserveStoredRuntimeCommandForTransport() = true, want false") + } +} diff --git a/internal/api/sse.go b/internal/api/sse.go index 671c7862ff..61d5b4e09a 100644 --- a/internal/api/sse.go +++ b/internal/api/sse.go @@ -2,13 +2,9 @@ package api import ( "context" - "crypto/tls" "encoding/json" "fmt" - "io" - "mime/multipart" "net/http" - "net/url" "reflect" "slices" "strings" @@ -67,6 +63,20 @@ type StringIDSender func(msg StringIDMessage) error // to StreamFunc. type StringIDStreamFunc[I any] func(hctx huma.Context, input *I, send StringIDSender) +type sseEventContract struct { + runtimeSample any + schemaSample any +} + +func (c sseEventContract) sseRuntimeSample() any { return c.runtimeSample } + +func (c sseEventContract) sseSchemaSample() any { return c.schemaSample } + +type sseSchemaOverride interface { + sseRuntimeSample() any + sseSchemaSample() any +} + // registerSSE registers an SSE operation like huma's sse.Register but with a // precheck hook that can return an HTTP error before the response is committed. // @@ -87,6 +97,7 @@ func registerSSE[I any]( precheck func(context.Context, *I) error, stream StreamFunc[I], ) { + normalizeSSEResponseHeaders(&op) typeToEvent := attachSSEResponseSchema(api, &op, eventTypeMap, huma.TypeInteger, "The event ID.") huma.Register(api, op, func(ctx context.Context, input *I) (*huma.StreamResponse, error) { @@ -97,22 +108,14 @@ func registerSSE[I any]( } return &huma.StreamResponse{ Body: func(hctx huma.Context) { - // Derive a cancelable context from the request context and - // plumb it into hctx so stream loops checking hctx.Context() - // exit promptly on send-error. - reqCtx, cancel := context.WithCancel(hctx.Context()) - defer cancel() - hctx = hctxWithCtx(reqCtx, hctx) - bw, encoder, flusher := beginSSEStream(hctx) - rawSend := func(msg sse.Message) error { + send := func(msg sse.Message) error { idLine := "" if msg.ID > 0 { idLine = fmt.Sprintf("id: %d\n", msg.ID) } return writeSSEFrame(bw, encoder, flusher, typeToEvent, idLine, msg.Data) } - send := cancelOnSendError(rawSend, cancel) stream(hctx, input, send) }, }, nil @@ -151,6 +154,7 @@ func registerSSEStringID[I any]( precheck func(context.Context, *I) error, stream StringIDStreamFunc[I], ) { + normalizeSSEResponseHeaders(&op) typeToEvent := attachSSEResponseSchema(api, &op, eventTypeMap, huma.TypeString, "The event ID (composite cursor).") huma.Register(api, op, func(ctx context.Context, input *I) (*huma.StreamResponse, error) { @@ -161,88 +165,73 @@ func registerSSEStringID[I any]( } return &huma.StreamResponse{ Body: func(hctx huma.Context) { - reqCtx, cancel := context.WithCancel(hctx.Context()) - defer cancel() - hctx = hctxWithCtx(reqCtx, hctx) - bw, encoder, flusher := beginSSEStream(hctx) - rawSend := func(msg StringIDMessage) error { + send := func(msg StringIDMessage) error { idLine := "" if msg.ID != "" { idLine = fmt.Sprintf("id: %s\n", msg.ID) } return writeSSEFrame(bw, encoder, flusher, typeToEvent, idLine, msg.Data) } - send := stringIDCancelOnSendError(rawSend, cancel) stream(hctx, input, send) }, }, nil }) } -// stringIDCancelOnSendError is the StringIDSender analog of cancelOnSendError. -// On first send failure it cancels the supplied context; subsequent calls -// short-circuit to the cached error. -func stringIDCancelOnSendError(send StringIDSender, cancel context.CancelFunc) StringIDSender { - var firstErr error - return func(msg StringIDMessage) error { - if firstErr != nil { - return firstErr +// sseStatusHeaders is the canonical catalog of custom response headers +// that stream handlers may emit via hctx.SetHeader. Each entry's key is +// the wire header name; the value is its human-readable description. +// Callers reference headers by name (see sseResponseHeaders) — the +// description travels with the name so a reader at the registration +// site sees only the list of headers the operation emits and each +// description has a single source of truth. +var sseStatusHeaders = map[string]string{ + "GC-Agent-Status": "Agent runtime status at the time streaming began. Emitted as \"stopped\" when the agent is not running (the stream then serves replayed transcript from the session log).", + "GC-Session-State": "Session state at the time streaming began (e.g. active, closed).", + "GC-Session-Status": "Runtime status at the time streaming began. Emitted as \"stopped\" when the session's underlying process is not running.", +} + +// sseResponseHeaders builds a Responses map declaring the named +// custom headers on the 200 response. Names must appear in +// sseStatusHeaders — the function panics if a caller references an +// undeclared header, so drift between SetHeader call sites and the +// declared contract surfaces at startup rather than in a stale spec. +func sseResponseHeaders(names ...string) map[string]*huma.Response { + headers := make(map[string]*huma.Param, len(names)) + for _, name := range names { + desc, ok := sseStatusHeaders[name] + if !ok { + panic("api: sse response header not in sseStatusHeaders catalog: " + name) } - if err := send(msg); err != nil { - firstErr = err - cancel() - return err + headers[name] = &huma.Param{ + Description: desc, + Schema: &huma.Schema{ + Type: "string", + Description: desc, + }, } - return nil + } + return map[string]*huma.Response{ + "200": {Headers: headers}, } } -// hctxWithCtx returns a huma.Context that reports the supplied ctx from -// its Context() method. Used by SSE registration to plumb a cancelable -// context into stream loops that poll hctx.Context().Done(). -// -// Note: we cannot embed huma.Context because the interface is literally -// named Context, which collides with our override method Context(). The -// override instead delegates every method explicitly. -func hctxWithCtx(ctx context.Context, hctx huma.Context) huma.Context { - return &hctxOverride{inner: hctx, ctx: ctx} -} - -// hctxOverride wraps a huma.Context to replace only the Context() method. -// All other methods delegate to the inner huma.Context. -type hctxOverride struct { - inner huma.Context - ctx context.Context -} - -func (h *hctxOverride) Operation() *huma.Operation { return h.inner.Operation() } -func (h *hctxOverride) Context() context.Context { return h.ctx } -func (h *hctxOverride) Method() string { return h.inner.Method() } -func (h *hctxOverride) Host() string { return h.inner.Host() } -func (h *hctxOverride) RemoteAddr() string { return h.inner.RemoteAddr() } -func (h *hctxOverride) URL() url.URL { return h.inner.URL() } -func (h *hctxOverride) Param(name string) string { return h.inner.Param(name) } -func (h *hctxOverride) Query(name string) string { return h.inner.Query(name) } -func (h *hctxOverride) Header(name string) string { return h.inner.Header(name) } -func (h *hctxOverride) EachHeader(cb func(name, value string)) { - h.inner.EachHeader(cb) -} -func (h *hctxOverride) BodyReader() io.Reader { return h.inner.BodyReader() } -func (h *hctxOverride) GetMultipartForm() (*multipart.Form, error) { - return h.inner.GetMultipartForm() -} - -func (h *hctxOverride) SetReadDeadline(deadline time.Time) error { - return h.inner.SetReadDeadline(deadline) +// normalizeSSEResponseHeaders ensures op.Responses["200"] exists with a +// non-nil Headers map so the pre-declared stream-status headers (set by +// the caller on the Operation literal) are preserved after +// attachSSEResponseSchema rebuilds Content. +func normalizeSSEResponseHeaders(op *huma.Operation) { + if op.Responses == nil { + op.Responses = map[string]*huma.Response{} + } + if op.Responses["200"] == nil { + op.Responses["200"] = &huma.Response{} + } + if op.Responses["200"].Headers == nil { + op.Responses["200"].Headers = map[string]*huma.Param{} + } } -func (h *hctxOverride) SetStatus(code int) { h.inner.SetStatus(code) } -func (h *hctxOverride) Status() int { return h.inner.Status() } -func (h *hctxOverride) AppendHeader(name, value string) { h.inner.AppendHeader(name, value) } -func (h *hctxOverride) SetHeader(name, value string) { h.inner.SetHeader(name, value) } -func (h *hctxOverride) BodyWriter() io.Writer { return h.inner.BodyWriter() } -func (h *hctxOverride) TLS() *tls.ConnectionState { return h.inner.TLS() } -func (h *hctxOverride) Version() huma.ProtoVersion { return h.inner.Version() } // attachSSEResponseSchema populates op.Responses with the text/event-stream // media block for the given event map. Returns the reverse-lookup map @@ -268,8 +257,10 @@ func attachSSEResponseSchema( typeToEvent := make(map[reflect.Type]string, len(eventTypeMap)) dataSchemas := make([]*huma.Schema, 0, len(eventTypeMap)) for k, v := range eventTypeMap { - vt := derefType(reflect.TypeOf(v)) - typeToEvent[vt] = k + runtimeSample, schemaSample := sseContractSamples(v) + runtimeType := derefType(reflect.TypeOf(runtimeSample)) + schemaType := derefType(reflect.TypeOf(schemaSample)) + typeToEvent[runtimeType] = k required := []string{"data"} if k != "" && k != "message" { required = append(required, "event") @@ -289,7 +280,7 @@ func attachSSEResponseSchema( "const": k, }, }, - "data": api.OpenAPI().Components.Schemas.Schema(vt, true, k), + "data": api.OpenAPI().Components.Schemas.Schema(schemaType, true, k), "retry": { Type: huma.TypeInteger, Description: "The retry time in milliseconds.", @@ -320,6 +311,13 @@ func attachSSEResponseSchema( return typeToEvent } +func sseContractSamples(v any) (any, any) { + if override, ok := v.(sseSchemaOverride); ok { + return override.sseRuntimeSample(), override.sseSchemaSample() + } + return v, v +} + // beginSSEStream sets the standard SSE headers on the huma response and // returns the underlying writer + JSON encoder + flusher the send // function will use per frame. diff --git a/internal/api/state.go b/internal/api/state.go index e3d6c647a2..9646ac2ce2 100644 --- a/internal/api/state.go +++ b/internal/api/state.go @@ -121,7 +121,9 @@ type ProviderUpdate struct { DisplayName *string Base **string Command *string + ACPCommand *string Args []string // nil = not set, non-nil = replace + ACPArgs []string // nil = not set, non-nil = replace ArgsAppend []string // nil = not set, non-nil = replace PromptMode *string PromptFlag *string diff --git a/internal/api/supervisor.go b/internal/api/supervisor.go index f0e3f40d12..e60a07538d 100644 --- a/internal/api/supervisor.go +++ b/internal/api/supervisor.go @@ -12,6 +12,7 @@ import ( "time" "github.com/danielgtaylor/huma/v2" + "github.com/gastownhall/gascity/internal/cityinit" "github.com/gastownhall/gascity/internal/events" ) @@ -33,6 +34,25 @@ type CityResolver interface { CityState(name string) State } +// TransientCityEventSource is an optional CityResolver extension +// that lets the supervisor-scope event multiplexer include event +// providers for cities that are registered but not yet (or no +// longer) in the Running set — newly scaffolded cities whose +// reconciler hasn't picked them up, cities currently running +// prepareCityForSupervisor, and cities whose init failed. Without +// this, /v0/events/stream subscribers can't observe city.created, +// city.ready, or city.init_failed for cities that aren't yet +// reporting Running=true through ListCities. +// +// Resolvers that implement this return one entry per transient +// city; the key is the city name, the value is an event provider +// backed by that city's .gc/events.jsonl file. The supervisor +// multiplexer adds these on top of the Running-city providers it +// already picks up via ListCities + CityState. +type TransientCityEventSource interface { + TransientCityEventProviders() map[string]events.Provider +} + // cachedCityServer pairs a State with its pre-built Server for caching. type cachedCityServer struct { state State @@ -53,11 +73,12 @@ type cachedCityServer struct { // to per-city Server.mux. Workspace services own their own HTTP // contracts and are explicitly excluded from the typed control plane. type SupervisorMux struct { - resolver CityResolver - readOnly bool - version string - startedAt time.Time - server *http.Server + resolver CityResolver + initializer cityinit.Initializer + readOnly bool + version string + startedAt time.Time + server *http.Server // Single Huma API (Phase 3.5 — Topology 1). Owns every typed // operation: supervisor-scope (/v0/cities, /health, /v0/readiness, @@ -76,20 +97,30 @@ type SupervisorMux struct { } // NewSupervisorMux creates a SupervisorMux that routes requests to cities -// resolved by the given CityResolver. -func NewSupervisorMux(resolver CityResolver, readOnly bool, version string, startedAt time.Time) *SupervisorMux { +// resolved by the given CityResolver. The initializer is invoked by the +// POST /v0/city handler to scaffold new cities in-process; passing nil +// is allowed for tests that don't exercise city creation (the handler +// returns 501 Not Implemented in that case). +func NewSupervisorMux(resolver CityResolver, initializer cityinit.Initializer, readOnly bool, version string, startedAt time.Time) *SupervisorMux { humaMux := http.NewServeMux() sm := &SupervisorMux{ - resolver: resolver, - readOnly: readOnly, - version: version, - startedAt: startedAt, - humaMux: humaMux, - humaAPI: newSupervisorHumaAPI(humaMux, readOnly), - cache: make(map[string]cachedCityServer), + resolver: resolver, + initializer: initializer, + readOnly: readOnly, + version: version, + startedAt: startedAt, + humaMux: humaMux, + humaAPI: newSupervisorHumaAPI(humaMux, readOnly), + cache: make(map[string]cachedCityServer), } sm.registerSupervisorRoutes() sm.registerCityRoutes() + documentProblemTypes(sm.humaAPI.OpenAPI()) + // Declare framework-level response headers (X-GC-Request-Id) via + // components.headers + $ref on every operation. Middleware writes + // the header at runtime; the spec describes the contract. Must run + // after all routes are registered. + registerFrameworkHeaders(sm.humaAPI) // /svc/* workspace-service pass-through. This is the single remaining // non-Huma registration on the supervisor — untyped by design (the // proxy passes bodies through to external service processes, which @@ -238,7 +269,13 @@ func (sm *SupervisorMux) getCityServer(name string, state State) *Server { } // buildMultiplexer creates a Multiplexer from all running cities' -// event providers. +// event providers plus any transient-city providers surfaced by a +// resolver that implements TransientCityEventSource. Including +// transient (pending init, in-progress, or failed) cities matters +// for clients that POST /v0/city and wait for city.created / +// city.ready / city.init_failed events on /v0/events/stream without +// polling — the city's own events.jsonl exists from Scaffold +// onward, but the city isn't in Running=true yet. func (sm *SupervisorMux) buildMultiplexer() *events.Multiplexer { mux := events.NewMultiplexer() cities := sm.resolver.ListCities() @@ -256,6 +293,14 @@ func (sm *SupervisorMux) buildMultiplexer() *events.Multiplexer { } mux.Add(c.Name, ep) } + if transient, ok := sm.resolver.(TransientCityEventSource); ok { + for name, ep := range transient.TransientCityEventProviders() { + if ep == nil { + continue + } + mux.Add(name, ep) + } + } return mux } diff --git a/internal/api/supervisor_city_routes.go b/internal/api/supervisor_city_routes.go index ac1ad13d36..eeb61a8949 100644 --- a/internal/api/supervisor_city_routes.go +++ b/internal/api/supervisor_city_routes.go @@ -81,6 +81,7 @@ func (sm *SupervisorMux) registerCityRoutes() { Path: cityScopePrefix + "/agent/{base}/output/stream", Summary: "Stream agent output in real time", Description: "Server-Sent Events stream of agent output (session log tail or tmux pane polling).", + Responses: sseResponseHeaders("GC-Agent-Status"), }, agentOutputEventMap, sseCityPrecheck(sm, (*Server).checkAgentOutputStream), sseCityStream(sm, (*Server).streamAgentOutput)) @@ -90,6 +91,7 @@ func (sm *SupervisorMux) registerCityRoutes() { Path: cityScopePrefix + "/agent/{dir}/{base}/output/stream", Summary: "Stream agent output in real time (qualified name)", Description: "Server-Sent Events stream of agent output for qualified (rig-prefixed) agent names.", + Responses: sseResponseHeaders("GC-Agent-Status"), }, agentOutputEventMap, sseCityPrecheck(sm, (*Server).checkAgentOutputStreamQualified), sseCityStream(sm, (*Server).streamAgentOutputQualified)) @@ -296,6 +298,7 @@ func (sm *SupervisorMux) registerCityRoutes() { "Streams turns (conversation format) or raw messages (JSONL format) " + "based on the format query parameter. Emits activity and pending events " + "for tool approval prompts.", + Responses: sseResponseHeaders("GC-Session-State", "GC-Session-Status"), }, sessionStreamEventMap(), sseCityPrecheck(sm, (*Server).checkSessionStream), sseCityStream(sm, (*Server).streamSession)) @@ -309,7 +312,10 @@ func (sm *SupervisorMux) registerCityRoutes() { Description: "Server-Sent Events stream of city events with optional workflow projections. " + "Supports reconnection via Last-Event-ID header or after_seq query param.", }, map[string]any{ - "event": eventStreamEnvelope{}, + "event": sseEventContract{ + runtimeSample: eventStreamEnvelope{}, + schemaSample: typedEventStreamEnvelopeSchema{}, + }, "heartbeat": HeartbeatEvent{}, }, sseCityPrecheck(sm, (*Server).checkEventStream), diff --git a/internal/api/supervisor_test.go b/internal/api/supervisor_test.go index 81321f7762..88176aebcf 100644 --- a/internal/api/supervisor_test.go +++ b/internal/api/supervisor_test.go @@ -45,7 +45,7 @@ func (f *fakeCityResolver) CityState(name string) State { func newTestSupervisorMux(t *testing.T, cities map[string]*fakeState) *SupervisorMux { t.Helper() resolver := &fakeCityResolver{cities: cities} - return NewSupervisorMux(resolver, false, "test", time.Now()) + return NewSupervisorMux(resolver, nil, false, "test", time.Now()) } func TestSupervisorCitiesList(t *testing.T) { @@ -387,6 +387,87 @@ func TestSupervisorPerCityEventStream(t *testing.T) { } } +func TestSupervisorPerCityEventStreamEmitsTypedEnvelopePayloadObject(t *testing.T) { + s := newFakeState(t) + s.cityName = "gc-work" + + sm := newTestSupervisorMux(t, map[string]*fakeState{ + "gc-work": s, + }) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + req := httptest.NewRequest("GET", "/v0/city/gc-work/events/stream", nil).WithContext(ctx) + rec := httptest.NewRecorder() + + done := make(chan struct{}) + go func() { + defer close(done) + sm.ServeHTTP(rec, req) + }() + + time.Sleep(50 * time.Millisecond) + payload, err := json.Marshal(MailEventPayload{Rig: "myrig"}) + if err != nil { + t.Fatalf("marshal payload: %v", err) + } + s.eventProv.(*events.Fake).Record(events.Event{ + Type: events.MailSent, + Actor: "tester", + Subject: "mail-1", + Payload: payload, + }) + + time.Sleep(100 * time.Millisecond) + cancel() + <-done + + frame := firstSSETestFrame(t, rec.Body.String(), "event") + if frame.ID != "1" { + t.Fatalf("SSE id = %q, want 1; body=%s", frame.ID, rec.Body.String()) + } + data := decodeSSETestData(t, frame) + if data["type"] != events.MailSent { + t.Fatalf("data.type = %v, want %s; data=%v", data["type"], events.MailSent, data) + } + if _, ok := data["city"]; ok { + t.Fatalf("per-city event data unexpectedly includes city: %v", data) + } + payloadObject, ok := data["payload"].(map[string]any) + if !ok { + t.Fatalf("data.payload = %#v, want JSON object", data["payload"]) + } + if payloadObject["rig"] != "myrig" { + t.Fatalf("payload.rig = %v, want myrig; payload=%v", payloadObject["rig"], payloadObject) + } +} + +func TestSupervisorPerCityEventStreamEmitsNoPayloadObject(t *testing.T) { + s := newFakeState(t) + s.cityName = "gc-work" + + sm := newTestSupervisorMux(t, map[string]*fakeState{ + "gc-work": s, + }) + + frame := firstSSEFrameAfterRecord(t, sm, "/v0/city/gc-work/events/stream", "event", func() { + s.eventProv.(*events.Fake).Record(events.Event{ + Type: events.SessionWoke, + Actor: "tester", + Subject: "session-1", + }) + }) + data := decodeSSETestData(t, frame) + if data["type"] != events.SessionWoke { + t.Fatalf("data.type = %v, want %s; data=%v", data["type"], events.SessionWoke, data) + } + payloadObject := assertJSONPayloadObject(t, data["payload"]) + if len(payloadObject) != 0 { + t.Fatalf("data.payload = %v, want empty object for NoPayload", payloadObject) + } +} + func TestSupervisorGlobalEventList(t *testing.T) { s1 := newFakeState(t) s1.cityName = "alpha" @@ -431,6 +512,71 @@ func TestSupervisorGlobalEventList(t *testing.T) { } } +func TestSupervisorEventListsEmitTypedPayloadObjects(t *testing.T) { + s := newFakeState(t) + s.cityName = "alpha" + payload, err := json.Marshal(MailEventPayload{Rig: "myrig"}) + if err != nil { + t.Fatalf("marshal payload: %v", err) + } + s.eventProv.(*events.Fake).Record(events.Event{ + Type: events.MailSent, + Actor: "tester", + Subject: "mail-1", + Payload: payload, + }) + s.eventProv.(*events.Fake).Record(events.Event{ + Type: events.SessionWoke, + Actor: "tester", + Subject: "session-1", + }) + + sm := newTestSupervisorMux(t, map[string]*fakeState{"alpha": s}) + + for _, tt := range []struct { + name string + path string + wantCity string + }{ + {name: "per-city", path: "/v0/city/alpha/events"}, + {name: "supervisor", path: "/v0/events", wantCity: "alpha"}, + } { + t.Run(tt.name, func(t *testing.T) { + req := httptest.NewRequest("GET", tt.path, nil) + rec := httptest.NewRecorder() + sm.ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusOK, rec.Body.String()) + } + + var resp struct { + Items []map[string]any `json:"items"` + Total int `json:"total"` + } + if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { + t.Fatalf("decode: %v", err) + } + if resp.Total != 2 { + t.Fatalf("total = %d, want 2; items=%v", resp.Total, resp.Items) + } + + mail := eventListItemByType(t, resp.Items, events.MailSent) + if tt.wantCity != "" && mail["city"] != tt.wantCity { + t.Fatalf("mail city = %v, want %s; item=%v", mail["city"], tt.wantCity, mail) + } + mailPayload := assertJSONPayloadObject(t, mail["payload"]) + if mailPayload["rig"] != "myrig" { + t.Fatalf("mail payload.rig = %v, want myrig; payload=%v", mailPayload["rig"], mailPayload) + } + + noPayload := assertJSONPayloadObject(t, eventListItemByType(t, resp.Items, events.SessionWoke)["payload"]) + if len(noPayload) != 0 { + t.Fatalf("session.woke payload = %v, want empty object", noPayload) + } + }) + } +} + func TestSupervisorGlobalEventListWithFilter(t *testing.T) { s1 := newFakeState(t) s1.cityName = "alpha" @@ -599,6 +745,93 @@ func TestSupervisorGlobalEventStreamCompositeCursor(t *testing.T) { } } +func TestSupervisorGlobalEventStreamEmitsTypedTaggedEnvelopePayloadObject(t *testing.T) { + s := newFakeState(t) + s.cityName = "alpha" + + sm := newTestSupervisorMux(t, map[string]*fakeState{ + "alpha": s, + }) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + req := httptest.NewRequest("GET", "/v0/events/stream", nil).WithContext(ctx) + rec := httptest.NewRecorder() + + done := make(chan struct{}) + go func() { + defer close(done) + sm.ServeHTTP(rec, req) + }() + + time.Sleep(50 * time.Millisecond) + payload, err := json.Marshal(MailEventPayload{Rig: "myrig"}) + if err != nil { + t.Fatalf("marshal payload: %v", err) + } + s.eventProv.(*events.Fake).Record(events.Event{ + Type: events.MailSent, + Actor: "tester", + Subject: "mail-1", + Payload: payload, + }) + + time.Sleep(100 * time.Millisecond) + cancel() + <-done + + frame := firstSSETestFrame(t, rec.Body.String(), "tagged_event") + if frame.ID != "alpha:1" { + t.Fatalf("SSE id = %q, want alpha:1; body=%s", frame.ID, rec.Body.String()) + } + data := decodeSSETestData(t, frame) + if data["type"] != events.MailSent { + t.Fatalf("data.type = %v, want %s; data=%v", data["type"], events.MailSent, data) + } + if data["city"] != "alpha" { + t.Fatalf("data.city = %v, want alpha; data=%v", data["city"], data) + } + payloadObject, ok := data["payload"].(map[string]any) + if !ok { + t.Fatalf("data.payload = %#v, want JSON object", data["payload"]) + } + if payloadObject["rig"] != "myrig" { + t.Fatalf("payload.rig = %v, want myrig; payload=%v", payloadObject["rig"], payloadObject) + } +} + +func TestSupervisorGlobalEventStreamEmitsNoPayloadObject(t *testing.T) { + s := newFakeState(t) + s.cityName = "alpha" + + sm := newTestSupervisorMux(t, map[string]*fakeState{ + "alpha": s, + }) + + frame := firstSSEFrameAfterRecord(t, sm, "/v0/events/stream", "tagged_event", func() { + s.eventProv.(*events.Fake).Record(events.Event{ + Type: events.SessionWoke, + Actor: "tester", + Subject: "session-1", + }) + }) + if frame.ID != "alpha:1" { + t.Fatalf("SSE id = %q, want alpha:1", frame.ID) + } + data := decodeSSETestData(t, frame) + if data["type"] != events.SessionWoke { + t.Fatalf("data.type = %v, want %s; data=%v", data["type"], events.SessionWoke, data) + } + if data["city"] != "alpha" { + t.Fatalf("data.city = %v, want alpha; data=%v", data["city"], data) + } + payloadObject := assertJSONPayloadObject(t, data["payload"]) + if len(payloadObject) != 0 { + t.Fatalf("data.payload = %v, want empty object for NoPayload", payloadObject) + } +} + func TestSupervisorGlobalEventStreamProjectsWorkflowMetadata(t *testing.T) { s1 := newFakeState(t) s1.cityName = "alpha" @@ -657,3 +890,104 @@ func TestSupervisorGlobalEventStreamProjectsWorkflowMetadata(t *testing.T) { t.Fatalf("global SSE body missing city tag: %s", body) } } + +type sseTestFrame struct { + Event string + ID string + Data string +} + +func firstSSETestFrame(t *testing.T, body, eventName string) sseTestFrame { + t.Helper() + + for _, frame := range parseSSETestFrames(body) { + if frame.Event == eventName { + return frame + } + } + t.Fatalf("SSE event %q not found in body: %s", eventName, body) + return sseTestFrame{} +} + +func firstSSEFrameAfterRecord(t *testing.T, h http.Handler, path, eventName string, record func()) sseTestFrame { + t.Helper() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + req := httptest.NewRequest("GET", path, nil).WithContext(ctx) + rec := httptest.NewRecorder() + done := make(chan struct{}) + go func() { + defer close(done) + h.ServeHTTP(rec, req) + }() + + time.Sleep(50 * time.Millisecond) + record() + time.Sleep(100 * time.Millisecond) + cancel() + <-done + + return firstSSETestFrame(t, rec.Body.String(), eventName) +} + +func parseSSETestFrames(body string) []sseTestFrame { + var frames []sseTestFrame + var current sseTestFrame + flush := func() { + if current.Event != "" || current.ID != "" || current.Data != "" { + frames = append(frames, current) + current = sseTestFrame{} + } + } + + scanner := bufio.NewScanner(strings.NewReader(body)) + for scanner.Scan() { + line := scanner.Text() + switch { + case line == "": + flush() + case strings.HasPrefix(line, "event: "): + current.Event = strings.TrimPrefix(line, "event: ") + case strings.HasPrefix(line, "id: "): + current.ID = strings.TrimPrefix(line, "id: ") + case strings.HasPrefix(line, "data: "): + current.Data = strings.TrimPrefix(line, "data: ") + } + } + flush() + return frames +} + +func decodeSSETestData(t *testing.T, frame sseTestFrame) map[string]any { + t.Helper() + + var data map[string]any + if err := json.Unmarshal([]byte(frame.Data), &data); err != nil { + t.Fatalf("decode SSE data for event %q: %v; data=%s", frame.Event, err, frame.Data) + } + return data +} + +func assertJSONPayloadObject(t *testing.T, raw any) map[string]any { + t.Helper() + + payloadObject, ok := raw.(map[string]any) + if !ok { + t.Fatalf("payload = %#v, want JSON object", raw) + } + return payloadObject +} + +func eventListItemByType(t *testing.T, items []map[string]any, eventType string) map[string]any { + t.Helper() + + for _, item := range items { + if item["type"] == eventType { + return item + } + } + t.Fatalf("event type %s not found in items: %v", eventType, items) + return nil +} diff --git a/internal/api/test_helpers_test.go b/internal/api/test_helpers_test.go index 5a64abb584..36ea0c7773 100644 --- a/internal/api/test_helpers_test.go +++ b/internal/api/test_helpers_test.go @@ -20,13 +20,13 @@ import ( // non-default naming, use newTestSupervisorMux directly. func newTestCityHandler(t *testing.T, state State) http.Handler { t.Helper() - return wrapTestSupervisorMiddleware(NewSupervisorMux(&stateCityResolver{state: state}, false, "test", time.Now())) + return wrapTestSupervisorMiddleware(NewSupervisorMux(&stateCityResolver{state: state}, nil, false, "test", time.Now())) } // newTestCityHandlerReadOnly is newTestCityHandler but with readOnly=true. func newTestCityHandlerReadOnly(t *testing.T, state State) http.Handler { t.Helper() - return wrapTestSupervisorMiddleware(NewSupervisorMux(&stateCityResolver{state: state}, true, "test", time.Now())) + return wrapTestSupervisorMiddleware(NewSupervisorMux(&stateCityResolver{state: state}, nil, true, "test", time.Now())) } // wrapTestSupervisorMiddleware applies the same middleware the supervisor's @@ -71,7 +71,7 @@ func cityURL(state State, path string) string { // Server so handler dispatch runs against that exact instance. func newTestCityHandlerWith(t *testing.T, state State, srv *Server) http.Handler { t.Helper() - sm := NewSupervisorMux(&stateCityResolver{state: state}, false, "test", time.Now()) + sm := NewSupervisorMux(&stateCityResolver{state: state}, nil, false, "test", time.Now()) sm.cacheMu.Lock() sm.cache[state.CityName()] = cachedCityServer{state: state, srv: srv} sm.cacheMu.Unlock() diff --git a/internal/api/worker_factory.go b/internal/api/worker_factory.go index ab4f152250..04318cd5a3 100644 --- a/internal/api/worker_factory.go +++ b/internal/api/worker_factory.go @@ -7,14 +7,10 @@ import ( func (s *Server) workerFactory(store beads.Store) (*worker.Factory, error) { cfg := s.state.Config() - var resolveTransport func(template string) string + var resolveTransport func(template, provider string) string if cfg != nil { - resolveTransport = func(template string) string { - agentCfg, ok := resolveSessionTemplateAgent(cfg, template) - if !ok { - return "" - } - return agentCfg.Session + resolveTransport = func(template, provider string) string { + return configuredSessionTransport(cfg, template, provider) } } return worker.NewFactory(worker.FactoryConfig{ @@ -24,7 +20,7 @@ func (s *Server) workerFactory(store beads.Store) (*worker.Factory, error) { SearchPaths: s.sessionLogPaths(), Recorder: s.state.EventProvider(), ResolveTransport: resolveTransport, - ResolveSessionRuntime: s.resolveWorkerSessionRuntime, + ResolveSessionRuntime: s.resolveWorkerSessionRuntimeWithMetadata, }) } diff --git a/internal/api/worker_factory_test.go b/internal/api/worker_factory_test.go index 12d9d3c9d0..b05b3797ed 100644 --- a/internal/api/worker_factory_test.go +++ b/internal/api/worker_factory_test.go @@ -2,6 +2,8 @@ package api import ( "context" + "os" + "path/filepath" "testing" "github.com/gastownhall/gascity/internal/config" @@ -37,7 +39,7 @@ func TestResolveWorkerSessionRuntimePreservesStoredResolvedCommandAndBackfillsCu ResumeCommand: "persisted resume {{.SessionKey}}", } - runtimeCfg, err := srv.resolveWorkerSessionRuntime(info, "") + runtimeCfg, err := srv.resolveWorkerSessionRuntime(info) if err != nil { t.Fatalf("resolveWorkerSessionRuntime: %v", err) } @@ -99,7 +101,7 @@ func TestResolveWorkerSessionRuntimeUsesResolvedCommandWhenPersistedCommandIsSta ResumeCommand: "persisted resume {{.SessionKey}}", } - runtimeCfg, err := srv.resolveWorkerSessionRuntime(info, "") + runtimeCfg, err := srv.resolveWorkerSessionRuntime(info) if err != nil { t.Fatalf("resolveWorkerSessionRuntime: %v", err) } @@ -135,6 +137,488 @@ func TestResolveWorkerSessionRuntimeUsesResolvedCommandWhenPersistedCommandIsSta } } +func TestResolveWorkerSessionRuntimeIncludesEffectiveMCPServers(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Agents[0].Provider = "resolved-worker" + fs.cfg.Agents[0].Session = "acp" + supportsACP := true + fs.cfg.Providers["resolved-worker"] = config.ProviderSpec{ + DisplayName: "Resolved Worker", + Command: "/bin/echo", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "filesystem.toml"), []byte(` +name = "filesystem" +command = "/bin/mcp" +args = ["--stdio"] +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + + srv := New(fs) + info := session.Info{ + ID: "sess-1", + Template: "myrig/worker", + Transport: "acp", + WorkDir: t.TempDir(), + } + + runtimeCfg, err := srv.resolveWorkerSessionRuntime(info) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntime: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntime() = nil") + } + if len(runtimeCfg.Hints.MCPServers) != 1 { + t.Fatalf("Hints.MCPServers len = %d, want 1", len(runtimeCfg.Hints.MCPServers)) + } + if got, want := runtimeCfg.Hints.MCPServers[0].Name, "filesystem"; got != want { + t.Fatalf("Hints.MCPServers[0].Name = %q, want %q", got, want) + } +} + +func TestResolveWorkerSessionRuntimeUsesStoredAgentNameForResumeMCPMaterialization(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Agents = []config.Agent{{ + Name: "ant", + Dir: "myrig", + Provider: "resolved-worker", + Session: "acp", + WorkDir: ".gc/worktrees/{{.Rig}}/ants/{{.AgentBase}}", + MinActiveSessions: intPtr(0), + MaxActiveSessions: intPtr(4), + }} + supportsACP := true + fs.cfg.Providers["resolved-worker"] = config.ProviderSpec{ + DisplayName: "Resolved Worker", + Command: "/bin/echo", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "identity.template.toml"), []byte(` +name = "identity" +command = "/bin/mcp" +args = ["{{.AgentName}}", "{{.WorkDir}}", "{{.TemplateName}}"] +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + + workDir := filepath.Join(fs.cityPath, ".gc", "worktrees", "myrig", "ants", "ant") + srv := New(fs) + info := session.Info{ + ID: "sess-1", + Template: "myrig/ant", + Alias: "ant", + AgentName: "myrig/ant-adhoc-123", + Transport: "acp", + WorkDir: workDir, + } + + runtimeCfg, err := srv.resolveWorkerSessionRuntime(info) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntime: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntime() = nil") + } + if len(runtimeCfg.Hints.MCPServers) != 1 { + t.Fatalf("Hints.MCPServers len = %d, want 1", len(runtimeCfg.Hints.MCPServers)) + } + if got, want := runtimeCfg.Hints.MCPServers[0].Args[0], info.AgentName; got != want { + t.Fatalf("Args[0] = %q, want %q", got, want) + } + if got, want := runtimeCfg.Hints.MCPServers[0].Args[1], workDir; got != want { + t.Fatalf("Args[1] = %q, want %q", got, want) + } + if got, want := runtimeCfg.Hints.MCPServers[0].Args[2], "myrig/ant"; got != want { + t.Fatalf("Args[2] = %q, want %q", got, want) + } +} + +func TestResolveWorkerSessionRuntimeFallsBackToStoredMCPServersWhenCatalogBreaks(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Agents = []config.Agent{{ + Name: "ant", + Dir: "myrig", + Provider: "resolved-worker", + Session: "acp", + WorkDir: ".gc/worktrees/{{.Rig}}/ants/{{.AgentBase}}", + MinActiveSessions: intPtr(0), + MaxActiveSessions: intPtr(4), + }} + supportsACP := true + fs.cfg.Providers["resolved-worker"] = config.ProviderSpec{ + DisplayName: "Resolved Worker", + Command: "/bin/echo", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "identity.template.toml"), []byte(` +name = "identity" +command = [broken +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + + workDir := filepath.Join(fs.cityPath, ".gc", "worktrees", "myrig", "ants", "ant") + metadata, err := session.WithStoredMCPMetadata(nil, "myrig/ant-adhoc-123", []runtime.MCPServerConfig{{ + Name: "identity", + Transport: runtime.MCPTransportStdio, + Command: "/bin/mcp", + Args: []string{"myrig/ant-adhoc-123", workDir, "myrig/ant"}, + }}) + if err != nil { + t.Fatalf("WithStoredMCPMetadata: %v", err) + } + + srv := New(fs) + info := session.Info{ + ID: "sess-1", + Template: "myrig/ant", + Alias: "ant", + AgentName: "myrig/ant-adhoc-123", + Transport: "acp", + WorkDir: workDir, + } + + runtimeCfg, err := srv.resolveWorkerSessionRuntimeWithMetadata(info, "", metadata) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntimeWithMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntimeWithMetadata() = nil") + } + if len(runtimeCfg.Hints.MCPServers) != 1 { + t.Fatalf("Hints.MCPServers len = %d, want 1", len(runtimeCfg.Hints.MCPServers)) + } + if got, want := runtimeCfg.Hints.MCPServers[0].Args[0], "myrig/ant-adhoc-123"; got != want { + t.Fatalf("Args[0] = %q, want %q", got, want) + } + if got, want := runtimeCfg.Hints.MCPServers[0].Args[1], workDir; got != want { + t.Fatalf("Args[1] = %q, want %q", got, want) + } + if got, want := runtimeCfg.Hints.MCPServers[0].Args[2], "myrig/ant"; got != want { + t.Fatalf("Args[2] = %q, want %q", got, want) + } +} + +func TestResolveWorkerSessionRuntimeFallsBackToRuntimeMCPServersSnapshotWhenCatalogBreaks(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Agents = []config.Agent{{ + Name: "ant", + Dir: "myrig", + Provider: "resolved-worker", + Session: "acp", + WorkDir: ".gc/worktrees/{{.Rig}}/ants/{{.AgentBase}}", + MinActiveSessions: intPtr(0), + MaxActiveSessions: intPtr(4), + }} + supportsACP := true + fs.cfg.Providers["resolved-worker"] = config.ProviderSpec{ + DisplayName: "Resolved Worker", + Command: "/bin/echo", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "identity.template.toml"), []byte(` +name = "identity" +command = [broken +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + + servers := []runtime.MCPServerConfig{{ + Name: "identity", + Transport: runtime.MCPTransportHTTP, + Command: "/bin/mcp", + Args: []string{"--api-key", "super-secret"}, + Env: map[string]string{ + "API_TOKEN": "super-secret", + }, + URL: "https://user:pass@example.invalid/mcp?token=abc123", + Headers: map[string]string{ + "Authorization": "Bearer secret", + }, + }} + metadata, err := session.WithStoredMCPMetadata(nil, "myrig/ant-adhoc-123", servers) + if err != nil { + t.Fatalf("WithStoredMCPMetadata: %v", err) + } + if err := session.PersistRuntimeMCPServersSnapshot(fs.cityPath, "sess-1", servers); err != nil { + t.Fatalf("PersistRuntimeMCPServersSnapshot: %v", err) + } + + srv := New(fs) + info := session.Info{ + ID: "sess-1", + Template: "myrig/ant", + Alias: "ant", + AgentName: "myrig/ant-adhoc-123", + Transport: "acp", + WorkDir: filepath.Join(fs.cityPath, ".gc", "worktrees", "myrig", "ants", "ant"), + } + + runtimeCfg, err := srv.resolveWorkerSessionRuntimeWithMetadata(info, "", metadata) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntimeWithMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntimeWithMetadata() = nil") + } + if len(runtimeCfg.Hints.MCPServers) != 1 { + t.Fatalf("Hints.MCPServers len = %d, want 1", len(runtimeCfg.Hints.MCPServers)) + } + if got, want := runtimeCfg.Hints.MCPServers[0].Args[1], "super-secret"; got != want { + t.Fatalf("Args[1] = %q, want %q", got, want) + } + if got, want := runtimeCfg.Hints.MCPServers[0].Env["API_TOKEN"], "super-secret"; got != want { + t.Fatalf("Env[API_TOKEN] = %q, want %q", got, want) + } + if got, want := runtimeCfg.Hints.MCPServers[0].Headers["Authorization"], "Bearer secret"; got != want { + t.Fatalf("Headers[Authorization] = %q, want %q", got, want) + } +} + +func TestResolveWorkerSessionRuntimeFallsBackToSanitizedStoredMCPServersWhenRuntimeSnapshotMissing(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Agents = []config.Agent{{ + Name: "ant", + Dir: "myrig", + Provider: "resolved-worker", + Session: "acp", + WorkDir: ".gc/worktrees/{{.Rig}}/ants/{{.AgentBase}}", + MinActiveSessions: intPtr(0), + MaxActiveSessions: intPtr(4), + }} + supportsACP := true + fs.cfg.Providers["resolved-worker"] = config.ProviderSpec{ + DisplayName: "Resolved Worker", + Command: "/bin/echo", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + fs.cfg.PackMCPDir = filepath.Join(fs.cityPath, "mcp") + if err := os.MkdirAll(fs.cfg.PackMCPDir, 0o755); err != nil { + t.Fatalf("MkdirAll(mcp): %v", err) + } + if err := os.WriteFile(filepath.Join(fs.cfg.PackMCPDir, "identity.template.toml"), []byte(` +name = "identity" +command = [broken +`), 0o644); err != nil { + t.Fatalf("WriteFile(mcp): %v", err) + } + + metadata, err := session.WithStoredMCPMetadata(nil, "myrig/ant-adhoc-123", []runtime.MCPServerConfig{{ + Name: "identity", + Transport: runtime.MCPTransportHTTP, + Command: "/bin/mcp", + Args: []string{"--serve", "--api-key", "super-secret"}, + Env: map[string]string{ + "API_TOKEN": "super-secret", + }, + URL: "https://user:pass@example.invalid/mcp?token=abc123", + Headers: map[string]string{ + "Authorization": "Bearer secret", + }, + }}) + if err != nil { + t.Fatalf("WithStoredMCPMetadata: %v", err) + } + + srv := New(fs) + info := session.Info{ + ID: "sess-1", + Template: "myrig/ant", + Alias: "ant", + AgentName: "myrig/ant-adhoc-123", + Transport: "acp", + WorkDir: filepath.Join(fs.cityPath, ".gc", "worktrees", "myrig", "ants", "ant"), + } + + runtimeCfg, err := srv.resolveWorkerSessionRuntimeWithMetadata(info, "", metadata) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntimeWithMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntimeWithMetadata() = nil") + } + if len(runtimeCfg.Hints.MCPServers) != 1 { + t.Fatalf("Hints.MCPServers len = %d, want 1", len(runtimeCfg.Hints.MCPServers)) + } + if got, want := runtimeCfg.Hints.MCPServers[0].Args, []string{"--serve"}; len(got) != len(want) || got[0] != want[0] { + t.Fatalf("Args = %#v, want %#v", got, want) + } + if len(runtimeCfg.Hints.MCPServers[0].Env) != 0 { + t.Fatalf("Env = %#v, want empty", runtimeCfg.Hints.MCPServers[0].Env) + } + if len(runtimeCfg.Hints.MCPServers[0].Headers) != 0 { + t.Fatalf("Headers = %#v, want empty", runtimeCfg.Hints.MCPServers[0].Headers) + } + if got, want := runtimeCfg.Hints.MCPServers[0].URL, "https://example.invalid/mcp"; got != want { + t.Fatalf("URL = %q, want %q", got, want) + } +} + +func TestResolveWorkerSessionRuntimeFallsBackToStoredCommandWhenTemplateOverridesInvalid(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Providers["test-agent"] = config.ProviderSpec{ + Command: "/bin/echo", + PathCheck: "true", + } + + srv := New(fs) + info := session.Info{ + ID: "sess-1", + Template: "myrig/worker", + Command: "/bin/echo --stored", + WorkDir: t.TempDir(), + } + + runtimeCfg, err := srv.resolveWorkerSessionRuntimeWithMetadata(info, "", map[string]string{ + "template_overrides": `{`, + }) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntimeWithMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntimeWithMetadata() = nil") + } + if got, want := runtimeCfg.Command, "/bin/echo --stored"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } +} + +func TestResolveWorkerSessionRuntimeUsesProviderACPDefaultWithoutTemplateSessionOverride(t *testing.T) { + supportsACP := true + fs := newSessionFakeState(t) + fs.cfg.Providers["test-agent"] = config.ProviderSpec{ + Command: "/bin/echo", + PathCheck: "true", + SupportsACP: &supportsACP, + ACPCommand: "/bin/echo", + ACPArgs: []string{"acp"}, + } + + srv := New(fs) + runtimeCfg, err := srv.resolveWorkerSessionRuntimeWithMetadata(session.Info{ + Template: "myrig/worker", + WorkDir: t.TempDir(), + }, "", nil) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntimeWithMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntimeWithMetadata() = nil") + } + if got, want := runtimeCfg.Command, "/bin/echo acp"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } +} + +func TestResolveWorkerSessionRuntimeFallsBackToPersistedRuntimeOnIncompleteResolvedConfig(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Providers["test-agent"] = config.ProviderSpec{ + ReadyPromptPrefix: "resolved-ready>", + ReadyDelayMs: 321, + } + + srv := New(fs) + info := session.Info{ + Template: "myrig/worker", + Command: "persisted-worker --dangerously-skip-permissions", + Provider: "persisted-provider", + WorkDir: "/tmp/persisted-workdir", + ResumeFlag: "--resume-persisted", + ResumeStyle: "subcommand", + ResumeCommand: "persisted resume {{.SessionKey}}", + } + + runtimeCfg, err := srv.resolveWorkerSessionRuntimeWithMetadata(info, "", nil) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntimeWithMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntimeWithMetadata() = nil") + } + if got, want := runtimeCfg.Command, info.Command; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } + if got, want := runtimeCfg.Provider, info.Provider; got != want { + t.Fatalf("Provider = %q, want %q", got, want) + } + if got, want := runtimeCfg.WorkDir, info.WorkDir; got != want { + t.Fatalf("WorkDir = %q, want %q", got, want) + } + if got, want := runtimeCfg.Resume.ResumeFlag, info.ResumeFlag; got != want { + t.Fatalf("Resume.ResumeFlag = %q, want %q", got, want) + } + if got, want := runtimeCfg.Resume.ResumeStyle, info.ResumeStyle; got != want { + t.Fatalf("Resume.ResumeStyle = %q, want %q", got, want) + } + if got, want := runtimeCfg.Resume.ResumeCommand, info.ResumeCommand; got != want { + t.Fatalf("Resume.ResumeCommand = %q, want %q", got, want) + } + if got, want := runtimeCfg.Hints.WorkDir, info.WorkDir; got != want { + t.Fatalf("Hints.WorkDir = %q, want %q", got, want) + } + if got, want := runtimeCfg.Hints.ReadyPromptPrefix, "resolved-ready>"; got != want { + t.Fatalf("Hints.ReadyPromptPrefix = %q, want %q", got, want) + } + if got, want := runtimeCfg.Hints.ReadyDelayMs, 321; got != want { + t.Fatalf("Hints.ReadyDelayMs = %d, want %d", got, want) + } +} + +func TestResolveWorkerSessionRuntimeFallsBackToPersistedProviderWhenCommandMissing(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Providers["test-agent"] = config.ProviderSpec{ + ReadyPromptPrefix: "resolved-ready>", + } + + srv := New(fs) + info := session.Info{ + Template: "myrig/worker", + Provider: "persisted-provider", + } + + runtimeCfg, err := srv.resolveWorkerSessionRuntimeWithMetadata(info, "", nil) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntimeWithMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntimeWithMetadata() = nil") + } + if got, want := runtimeCfg.Command, info.Provider; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } + if got, want := runtimeCfg.Provider, info.Provider; got != want { + t.Fatalf("Provider = %q, want %q", got, want) + } +} + func TestWorkerFactorySessionByIDUsesResolvedTemplateRuntime(t *testing.T) { fs := newSessionFakeState(t) fs.cfg.Agents[0].Provider = "resolved-worker" diff --git a/internal/beads/bdstore.go b/internal/beads/bdstore.go index 2b799b118c..78b2a81402 100644 --- a/internal/beads/bdstore.go +++ b/internal/beads/bdstore.go @@ -290,21 +290,29 @@ func (m *StringMap) UnmarshalJSON(data []byte) error { // bdIssue is the JSON shape returned by bd CLI commands. We decode only the // fields Gas City cares about; all others are silently ignored. type bdIssue struct { - ID string `json:"id"` - Title string `json:"title"` - Status string `json:"status"` - IssueType string `json:"issue_type"` - Priority *int `json:"priority,omitempty"` - CreatedAt time.Time `json:"created_at"` - Assignee string `json:"assignee"` - From string `json:"from"` - ParentID string `json:"parent"` - Ref string `json:"ref"` - Needs []string `json:"needs"` - Description string `json:"description"` - Labels []string `json:"labels"` - Metadata StringMap `json:"metadata,omitempty"` - Dependencies []Dep `json:"dependencies,omitempty"` + ID string `json:"id"` + Title string `json:"title"` + Status string `json:"status"` + IssueType string `json:"issue_type"` + Priority *int `json:"priority,omitempty"` + CreatedAt time.Time `json:"created_at"` + Assignee string `json:"assignee"` + From string `json:"from"` + ParentID string `json:"parent"` + Ref string `json:"ref"` + Needs []string `json:"needs"` + Description string `json:"description"` + Labels []string `json:"labels"` + Metadata StringMap `json:"metadata,omitempty"` + Dependencies []bdIssueDep `json:"dependencies,omitempty"` +} + +type bdIssueDep struct { + IssueID string `json:"issue_id"` + DependsOnID string `json:"depends_on_id"` + Type string `json:"type"` + ID string `json:"id"` + DependencyType string `json:"dependency_type"` } // parseIssuesTolerant unmarshals a JSON array of bdIssue objects, skipping @@ -354,6 +362,16 @@ func (b *bdIssue) toBead() Bead { if from == "" && b.Metadata != nil { from = b.Metadata["from"] } + deps := b.normalizedDependencies() + parentID := b.ParentID + if parentID == "" { + for _, dep := range deps { + if dep.IssueID == b.ID && dep.Type == "parent-child" { + parentID = dep.DependsOnID + break + } + } + } return Bead{ ID: b.ID, Title: b.Title, @@ -363,16 +381,49 @@ func (b *bdIssue) toBead() Bead { CreatedAt: b.CreatedAt.Truncate(time.Second), Assignee: b.Assignee, From: from, - ParentID: b.ParentID, + ParentID: parentID, Ref: b.Ref, Needs: b.Needs, Description: b.Description, Labels: b.Labels, Metadata: b.Metadata, - Dependencies: append([]Dep(nil), b.Dependencies...), + Dependencies: deps, } } +func (b *bdIssue) normalizedDependencies() []Dep { + if len(b.Dependencies) == 0 { + return nil + } + deps := make([]Dep, 0, len(b.Dependencies)) + for _, raw := range b.Dependencies { + issueID := strings.TrimSpace(raw.IssueID) + if issueID == "" && raw.ID != "" { + issueID = b.ID + } + dependsOnID := strings.TrimSpace(raw.DependsOnID) + if dependsOnID == "" { + dependsOnID = strings.TrimSpace(raw.ID) + } + depType := strings.TrimSpace(raw.Type) + if depType == "" { + depType = strings.TrimSpace(raw.DependencyType) + } + if issueID == "" || dependsOnID == "" { + continue + } + if depType == "" { + depType = "blocks" + } + deps = append(deps, Dep{ + IssueID: issueID, + DependsOnID: dependsOnID, + Type: depType, + }) + } + return deps +} + // isBdNotFound returns true if the error from bd CLI indicates a "not found" condition. // bd uses several phrasings: "no issue found", "issue not found", "not found". func isBdNotFound(err error) bool { @@ -456,8 +507,10 @@ func (s *BdStore) Create(b Bead) (Bead, error) { if created.Priority == nil && b.Priority != nil { created.Priority = cloneIntPtr(b.Priority) } - if created.Metadata == nil && len(metadata) > 0 { - created.Metadata = metadata + if len(metadata) > 0 { + if created.Metadata == nil { + created.Metadata = maps.Clone(metadata) + } } return created, nil } diff --git a/internal/beads/bdstore_test.go b/internal/beads/bdstore_test.go index 4d748f4a8e..8f49b14f5a 100644 --- a/internal/beads/bdstore_test.go +++ b/internal/beads/bdstore_test.go @@ -969,10 +969,10 @@ func TestBdStoreCreateWithLabels(t *testing.T) { var gotArgs []string runner := func(_, _ string, args ...string) ([]byte, error) { gotArgs = args - return []byte(`{"id":"bd-x","title":"test","status":"open","issue_type":"convoy","created_at":"2025-01-15T10:30:00Z","labels":["owned"]}`), nil + return []byte(`{"id":"bd-x","title":"test","status":"open","issue_type":"convoy","created_at":"2025-01-15T10:30:00Z"}`), nil } s := beads.NewBdStore("/city", runner) - _, err := s.Create(beads.Bead{Title: "test", Type: "convoy", Labels: []string{"owned"}}) + created, err := s.Create(beads.Bead{Title: "test", Type: "convoy", Labels: []string{"owned"}}) if err != nil { t.Fatal(err) } @@ -980,6 +980,9 @@ func TestBdStoreCreateWithLabels(t *testing.T) { if !strings.Contains(args, "--labels owned") { t.Errorf("args = %q, want to contain '--labels owned'", args) } + if len(created.Labels) != 0 { + t.Errorf("created.Labels = %#v, want empty until backend confirms labels", created.Labels) + } } func TestBdStoreCreateWithMultipleLabelsUsesSingleFlag(t *testing.T) { @@ -1009,7 +1012,7 @@ func TestBdStoreCreateWithParentID(t *testing.T) { return []byte(`{"id":"bd-x","title":"test","status":"open","issue_type":"task","created_at":"2025-01-15T10:30:00Z"}`), nil } s := beads.NewBdStore("/city", runner) - _, err := s.Create(beads.Bead{Title: "test", ParentID: "bd-parent-1"}) + created, err := s.Create(beads.Bead{Title: "test", ParentID: "bd-parent-1"}) if err != nil { t.Fatal(err) } @@ -1017,6 +1020,44 @@ func TestBdStoreCreateWithParentID(t *testing.T) { if !strings.Contains(args, "--parent bd-parent-1") { t.Errorf("args = %q, want to contain '--parent bd-parent-1'", args) } + if created.ParentID != "" { + t.Errorf("created.ParentID = %q, want empty until backend confirms parent", created.ParentID) + } +} + +func TestBdStoreCreateDoesNotBackfillUnconfirmedFields(t *testing.T) { + runner := func(_, _ string, _ ...string) ([]byte, error) { + return []byte(`{"id":"bd-x","title":"test","status":"open","issue_type":"task","created_at":"2025-01-15T10:30:00Z","metadata":{"accepted":"true"}}`), nil + } + s := beads.NewBdStore("/city", runner) + created, err := s.Create(beads.Bead{ + Title: "test", + Description: "local description", + ParentID: "bd-parent-1", + Labels: []string{"owned"}, + Needs: []string{"bd-2"}, + Metadata: map[string]string{ + "local": "value", + }, + }) + if err != nil { + t.Fatal(err) + } + if created.Description != "" { + t.Fatalf("created.Description = %q, want empty until backend confirms it", created.Description) + } + if created.ParentID != "" { + t.Fatalf("created.ParentID = %q, want empty until backend confirms it", created.ParentID) + } + if len(created.Labels) != 0 { + t.Fatalf("created.Labels = %#v, want empty until backend confirms them", created.Labels) + } + if len(created.Needs) != 0 { + t.Fatalf("created.Needs = %#v, want empty until backend confirms them", created.Needs) + } + if len(created.Metadata) != 1 || created.Metadata["accepted"] != "true" { + t.Fatalf("created.Metadata = %#v, want backend metadata only", created.Metadata) + } } func TestBdStoreDepAddParentChildAlreadyParentedIsNoop(t *testing.T) { @@ -1041,6 +1082,92 @@ func TestBdStoreDepAddParentChildAlreadyParentedIsNoop(t *testing.T) { } } +func TestBdStoreGetNormalizesShowStyleDependencies(t *testing.T) { + runner := fakeRunner(map[string]struct { + out []byte + err error + }{ + `bd show --json bd-child`: { + out: []byte(`[ + { + "id":"bd-child", + "title":"child", + "status":"open", + "issue_type":"task", + "created_at":"2025-01-15T10:30:00Z", + "dependencies":[ + { + "id":"bd-parent", + "title":"parent", + "status":"open", + "issue_type":"task", + "dependency_type":"parent-child" + }, + { + "issue_id":"", + "depends_on_id":"", + "type":"" + } + ], + "parent":"bd-parent" + } + ]`), + }, + }) + s := beads.NewBdStore("/city", runner) + + got, err := s.Get("bd-child") + if err != nil { + t.Fatalf("Get: %v", err) + } + if len(got.Dependencies) != 1 { + t.Fatalf("Dependencies = %#v, want one normalized dependency", got.Dependencies) + } + dep := got.Dependencies[0] + if dep.IssueID != "bd-child" || dep.DependsOnID != "bd-parent" || dep.Type != "parent-child" { + t.Fatalf("dependency = %+v, want child -> parent parent-child", dep) + } +} + +func TestBdStoreListInfersParentFromParentChildDependency(t *testing.T) { + runner := fakeRunner(map[string]struct { + out []byte + err error + }{ + `bd list --json --label=mc-live-contract --include-infra --include-gates --limit 50`: { + out: []byte(`[ + { + "id":"bd-child", + "title":"child", + "status":"open", + "issue_type":"task", + "created_at":"2025-01-15T10:30:00Z", + "labels":["mc-live-contract"], + "dependencies":[ + { + "issue_id":"bd-child", + "depends_on_id":"bd-parent", + "type":"parent-child" + } + ] + } + ]`), + }, + }) + s := beads.NewBdStore("/city", runner) + + got, err := s.List(beads.ListQuery{Label: "mc-live-contract", Limit: 50}) + if err != nil { + t.Fatalf("List: %v", err) + } + if len(got) != 1 { + t.Fatalf("List returned %d beads, want 1", len(got)) + } + if got[0].ParentID != "bd-parent" { + t.Fatalf("ParentID = %q, want bd-parent", got[0].ParentID) + } +} + func TestBdStoreCreateNoLabelsNoParent(t *testing.T) { var gotArgs []string runner := func(_, _ string, args ...string) ([]byte, error) { diff --git a/internal/beads/caching_store.go b/internal/beads/caching_store.go index 52cddf6c26..2cc9a46ebb 100644 --- a/internal/beads/caching_store.go +++ b/internal/beads/caching_store.go @@ -28,8 +28,11 @@ type CachingStore struct { beads map[string]Bead deps map[string][]Dep dirty map[string]struct{} + beadSeq map[string]uint64 + deletedSeq map[string]uint64 state cacheState lastFreshAt time.Time + mutationSeq uint64 reconciling atomic.Bool syncFailures int @@ -92,23 +95,41 @@ func NewCachingStoreForTest(backing Store, onChange func(eventType, beadID strin func newCachingStore(backing Store, onChange func(eventType, beadID string, payload json.RawMessage)) *CachingStore { return &CachingStore{ - backing: backing, - beads: make(map[string]Bead), - deps: make(map[string][]Dep), - dirty: make(map[string]struct{}), - onChange: onChange, + backing: backing, + beads: make(map[string]Bead), + deps: make(map[string][]Dep), + dirty: make(map[string]struct{}), + beadSeq: make(map[string]uint64), + deletedSeq: make(map[string]uint64), + onChange: onChange, problemf: func(msg string) { log.Printf("beads cache: %s", msg) }, } } +func (c *CachingStore) noteMutationLocked(ids ...string) uint64 { + c.mutationSeq++ + seq := c.mutationSeq + for _, id := range ids { + if id == "" { + continue + } + c.beadSeq[id] = seq + } + return seq +} + // PrimeActive loads all non-closed beads (open + in_progress) into the // cache. These are fast indexed queries that populate enough data for // startup paths without waiting for a full scan. The cache enters // cachePartial state: filtered active queries and Get hit cache for primed // beads, while closed-bead queries still delegate to the backing store. func (c *CachingStore) PrimeActive() error { + c.mu.RLock() + startSeq := c.mutationSeq + c.mu.RUnlock() + var all []Bead for _, status := range []string{"open", "in_progress"} { beads, err := c.backing.List(ListQuery{Status: status}) @@ -121,7 +142,16 @@ func (c *CachingStore) PrimeActive() error { c.mu.Lock() defer c.mu.Unlock() for _, b := range all { + if c.mutationSeq != startSeq { + if c.deletedSeq[b.ID] > startSeq { + continue + } + if _, exists := c.beads[b.ID]; exists { + continue + } + } c.beads[b.ID] = cloneBead(b) + delete(c.deletedSeq, b.ID) } if c.state == cacheUninitialized { c.state = cachePartial @@ -135,6 +165,10 @@ func (c *CachingStore) PrimeActive() error { // Retries up to 3 times on failure since bd list can time out under // concurrent dolt load. func (c *CachingStore) Prime(_ context.Context) error { + c.mu.RLock() + startSeq := c.mutationSeq + c.mu.RUnlock() + var all []Bead var err error for attempt := 1; attempt <= 3; attempt++ { @@ -164,9 +198,28 @@ func (c *CachingStore) Prime(_ context.Context) error { now := time.Now() c.mu.Lock() defer c.mu.Unlock() - c.beads = beadMap - c.deps = depMap - c.dirty = make(map[string]struct{}) + if c.mutationSeq == startSeq { + c.beads = beadMap + c.deps = depMap + c.dirty = make(map[string]struct{}) + c.beadSeq = make(map[string]uint64) + c.deletedSeq = make(map[string]uint64) + } else { + for id, b := range beadMap { + if c.deletedSeq[id] > startSeq { + continue + } + if _, exists := c.beads[id]; exists { + continue + } + c.beads[id] = b + delete(c.deletedSeq, id) + delete(c.beadSeq, id) + if deps, ok := depMap[id]; ok { + c.deps[id] = deps + } + } + } c.state = cacheLive c.syncFailures = 0 c.stats.SyncFailures = 0 diff --git a/internal/beads/caching_store_events.go b/internal/beads/caching_store_events.go index 3d2c932fae..8ef6a0d4d4 100644 --- a/internal/beads/caching_store_events.go +++ b/internal/beads/caching_store_events.go @@ -2,6 +2,7 @@ package beads import ( "encoding/json" + "errors" "fmt" "maps" "slices" @@ -17,39 +18,71 @@ func (c *CachingStore) ApplyEvent(eventType string, payload json.RawMessage) { return } - b, err := decodeCacheEvent(payload) + patch, fields, err := decodeCacheEvent(payload) if err != nil { c.recordProblem(fmt.Sprintf("apply %s event", eventType), err) return } + c.mu.RLock() + if c.state != cacheLive { + c.mu.RUnlock() + return + } + _, cached := c.beads[patch.ID] + c.mu.RUnlock() + + b := patch + if !cached { + if fresh, err := c.backing.Get(patch.ID); err == nil { + b = fresh + } else if !errors.Is(err, ErrNotFound) { + c.recordProblem(fmt.Sprintf("refresh %s event", eventType), err) + } + } + c.mu.Lock() defer c.mu.Unlock() if c.state != cacheLive { return } + if current, ok := c.beads[patch.ID]; ok { + b = mergeCacheEventPatch(current, patch, fields) + } + mutated := false switch eventType { case "bead.created": if _, exists := c.beads[b.ID]; !exists { + c.noteMutationLocked(b.ID) c.beads[b.ID] = cloneBead(b) delete(c.dirty, b.ID) - c.updateStatsLocked() + delete(c.deletedSeq, b.ID) } + c.updateStatsLocked() + mutated = true case "bead.updated": + c.noteMutationLocked(b.ID) c.beads[b.ID] = cloneBead(b) delete(c.dirty, b.ID) + delete(c.deletedSeq, b.ID) + mutated = true case "bead.closed": + c.noteMutationLocked(b.ID) if _, exists := c.beads[b.ID]; !exists { c.updateStatsLocked() } c.beads[b.ID] = cloneBead(b) delete(c.dirty, b.ID) + delete(c.deletedSeq, b.ID) + mutated = true default: return } - c.markFreshLocked(time.Now()) + if mutated { + c.markFreshLocked(time.Now()) + } } // ApplyDepEvent updates the dep cache for a bead. Call after dep @@ -60,33 +93,91 @@ func (c *CachingStore) ApplyDepEvent(beadID string, deps []Dep) { if c.state != cacheLive { return } + c.noteMutationLocked(beadID) c.deps[beadID] = cloneDeps(deps) delete(c.dirty, beadID) + delete(c.deletedSeq, beadID) c.markFreshLocked(time.Now()) c.updateStatsLocked() } -func decodeCacheEvent(payload json.RawMessage) (Bead, error) { +func mergeCacheEventPatch(base, patch Bead, fields map[string]json.RawMessage) Bead { + merged := cloneBead(base) + if hasCacheEventField(fields, "title") { + merged.Title = patch.Title + } + if hasCacheEventField(fields, "status") { + merged.Status = patch.Status + } + if hasCacheEventField(fields, "issue_type") || hasCacheEventField(fields, "type") { + merged.Type = patch.Type + } + if hasCacheEventField(fields, "priority") { + merged.Priority = cloneIntPtr(patch.Priority) + } + if hasCacheEventField(fields, "created_at") { + merged.CreatedAt = patch.CreatedAt + } + if hasCacheEventField(fields, "assignee") { + merged.Assignee = patch.Assignee + } + if hasCacheEventField(fields, "from") { + merged.From = patch.From + } + if hasCacheEventField(fields, "parent") { + merged.ParentID = patch.ParentID + } + if hasCacheEventField(fields, "ref") { + merged.Ref = patch.Ref + } + if hasCacheEventField(fields, "needs") { + merged.Needs = slices.Clone(patch.Needs) + } + if hasCacheEventField(fields, "description") { + merged.Description = patch.Description + } + if hasCacheEventField(fields, "labels") { + merged.Labels = slices.Clone(patch.Labels) + } + if hasCacheEventField(fields, "metadata") { + merged.Metadata = maps.Clone(patch.Metadata) + } + if hasCacheEventField(fields, "dependencies") { + merged.Dependencies = slices.Clone(patch.Dependencies) + } + return merged +} + +func hasCacheEventField(fields map[string]json.RawMessage, name string) bool { + _, ok := fields[name] + return ok +} + +func decodeCacheEvent(payload json.RawMessage) (Bead, map[string]json.RawMessage, error) { + var fields map[string]json.RawMessage + if err := json.Unmarshal(payload, &fields); err != nil { + return Bead{}, nil, err + } var wire struct { Bead Metadata StringMap `json:"metadata,omitempty"` TypeCompat string `json:"type,omitempty"` } if err := json.Unmarshal(payload, &wire); err != nil { - return Bead{}, err + return Bead{}, nil, err } b := wire.Bead if wire.Metadata != nil { b.Metadata = map[string]string(wire.Metadata) } if b.ID == "" { - return Bead{}, fmt.Errorf("missing bead id") + return Bead{}, nil, fmt.Errorf("missing bead id") } // bd hook payloads use "issue_type" while exec-style payloads may use "type". if b.Type == "" && wire.TypeCompat != "" { b.Type = wire.TypeCompat } - return b, nil + return b, fields, nil } func (c *CachingStore) notifyChange(eventType string, b Bead) { diff --git a/internal/beads/caching_store_reads.go b/internal/beads/caching_store_reads.go index 2c76b06007..3413e3e988 100644 --- a/internal/beads/caching_store_reads.go +++ b/internal/beads/caching_store_reads.go @@ -14,8 +14,15 @@ func (c *CachingStore) List(query ListQuery) ([]Bead, error) { if !query.HasFilter() && !query.AllowScan { return nil, fmt.Errorf("listing beads: %w", ErrQueryRequiresScan) } - if query.Live { - return c.backing.List(query) + if query.Live || query.ParentID != "" { + c.mu.RLock() + startSeq := c.mutationSeq + c.mu.RUnlock() + items, err := c.backing.List(query) + if err == nil { + items = c.refreshCachedBeads(query, startSeq, items) + } + return items, err } c.mu.RLock() @@ -76,6 +83,101 @@ func (c *CachingStore) List(query ListQuery) ([]Bead, error) { return c.backing.List(query) } +func (c *CachingStore) refreshCachedBeads(query ListQuery, startSeq uint64, items []Bead) []Bead { + refreshedParents := make(map[string]Bead) + removedParents := make(map[string]struct{}) + for _, id := range c.staleParentCacheIDs(query.ParentID, items) { + fresh, err := c.backing.Get(id) + switch { + case err == nil: + refreshedParents[id] = cloneBead(fresh) + case errors.Is(err, ErrNotFound): + removedParents[id] = struct{}{} + default: + c.recordProblem("refresh parent cache during list", fmt.Errorf("%s: %w", id, err)) + } + } + if len(items) == 0 && len(refreshedParents) == 0 && len(removedParents) == 0 { + return items + } + c.mu.Lock() + defer c.mu.Unlock() + if c.state != cacheLive && c.state != cachePartial { + return items + } + refreshed := make([]Bead, 0, len(items)) + for _, item := range items { + switch { + case c.deletedSeq[item.ID] > startSeq: + continue + case c.beadSeq[item.ID] > startSeq: + current, ok := c.beads[item.ID] + if ok && query.Matches(current) { + refreshed = append(refreshed, cloneBead(current)) + } + continue + } + c.beads[item.ID] = cloneBead(item) + delete(c.dirty, item.ID) + delete(c.deletedSeq, item.ID) + delete(c.beadSeq, item.ID) + if query.Matches(item) { + refreshed = append(refreshed, cloneBead(item)) + } + } + for id, bead := range refreshedParents { + if c.deletedSeq[id] > startSeq || c.beadSeq[id] > startSeq { + continue + } + c.beads[id] = bead + delete(c.dirty, id) + delete(c.deletedSeq, id) + delete(c.beadSeq, id) + } + for id := range removedParents { + if c.deletedSeq[id] > startSeq || c.beadSeq[id] > startSeq { + continue + } + delete(c.beads, id) + delete(c.deps, id) + delete(c.dirty, id) + delete(c.deletedSeq, id) + delete(c.beadSeq, id) + } + c.markFreshLocked(time.Now()) + c.updateStatsLocked() + return refreshed +} + +func (c *CachingStore) staleParentCacheIDs(parentID string, fresh []Bead) []string { + if parentID == "" { + return nil + } + + freshIDs := make(map[string]struct{}, len(fresh)) + for _, item := range fresh { + freshIDs[item.ID] = struct{}{} + } + + c.mu.RLock() + defer c.mu.RUnlock() + if c.state != cacheLive && c.state != cachePartial { + return nil + } + + var stale []string + for id, bead := range c.beads { + if bead.ParentID != parentID { + continue + } + if _, ok := freshIDs[id]; ok { + continue + } + stale = append(stale, id) + } + return stale +} + // ListOpen returns all cached beads, optionally filtered by status. func (c *CachingStore) ListOpen(status ...string) ([]Bead, error) { query := ListQuery{AllowScan: true} @@ -90,14 +192,37 @@ func (c *CachingStore) Get(id string) (Bead, error) { c.mu.RLock() if c.state == cacheLive || c.state == cachePartial { if _, ok := c.dirty[id]; ok { + startSeq := c.mutationSeq c.mu.RUnlock() fresh, err := c.backing.Get(id) if err != nil { return Bead{}, err } c.mu.Lock() + if c.state != cacheLive && c.state != cachePartial { + c.mu.Unlock() + return fresh, nil + } + switch { + case c.deletedSeq[id] > startSeq: + c.mu.Unlock() + return Bead{}, ErrNotFound + case c.beadSeq[id] > startSeq: + if _, stillDirty := c.dirty[id]; stillDirty { + c.mu.Unlock() + return c.backing.Get(id) + } + if current, ok := c.beads[id]; ok { + c.mu.Unlock() + return cloneBead(current), nil + } + c.mu.Unlock() + return Bead{}, ErrNotFound + } c.beads[id] = cloneBead(fresh) delete(c.dirty, id) + delete(c.deletedSeq, id) + delete(c.beadSeq, id) c.markFreshLocked(time.Now()) c.updateStatsLocked() c.mu.Unlock() diff --git a/internal/beads/caching_store_reconcile.go b/internal/beads/caching_store_reconcile.go index 304af232ad..227187a44c 100644 --- a/internal/beads/caching_store_reconcile.go +++ b/internal/beads/caching_store_reconcile.go @@ -62,6 +62,11 @@ func (c *CachingStore) nextReconcileDelay(now time.Time) time.Duration { func (c *CachingStore) runReconciliation() { start := time.Now() + + c.mu.RLock() + startSeq := c.mutationSeq + c.mu.RUnlock() + fresh, err := c.backing.List(ListQuery{AllowScan: true}) if err != nil { c.mu.Lock() @@ -86,6 +91,84 @@ func (c *CachingStore) runReconciliation() { } c.mu.Lock() + if c.mutationSeq != startSeq { + var adds, removes, updates int64 + notifications := make([]cacheNotification, 0, len(freshByID)) + + for id, freshBead := range freshByID { + if c.deletedSeq[id] > startSeq || c.beadSeq[id] > startSeq { + continue + } + + old, exists := c.beads[id] + switch { + case !exists: + adds++ + notifications = append(notifications, cacheNotification{ + eventType: "bead.created", + bead: cloneBead(freshBead), + }) + case beadChanged(old, freshBead): + updates++ + notifications = append(notifications, cacheNotification{ + eventType: "bead.updated", + bead: cloneBead(freshBead), + }) + case depErr == nil && depsChanged(c.deps[id], depMap[id]): + updates++ + notifications = append(notifications, cacheNotification{ + eventType: "bead.updated", + bead: cloneBead(freshBead), + }) + } + + c.beads[id] = cloneBead(freshBead) + if depErr == nil { + c.deps[id] = cloneDeps(depMap[id]) + } + delete(c.dirty, id) + delete(c.deletedSeq, id) + delete(c.beadSeq, id) + } + + for id, old := range c.beads { + if _, exists := freshByID[id]; exists { + continue + } + if c.deletedSeq[id] > startSeq || c.beadSeq[id] > startSeq { + continue + } + removes++ + closed := cloneBead(old) + closed.Status = "closed" + notifications = append(notifications, cacheNotification{ + eventType: "bead.closed", + bead: closed, + }) + delete(c.beads, id) + delete(c.deps, id) + delete(c.dirty, id) + delete(c.deletedSeq, id) + delete(c.beadSeq, id) + } + + c.syncFailures = 0 + if c.state == cacheDegraded { + c.state = cacheLive + } + now := time.Now() + durMs := float64(time.Since(start).Microseconds()) / 1000.0 + c.stats.LastReconcileAt = now + c.stats.LastReconcileMs = durMs + c.stats.Adds += adds + c.stats.Removes += removes + c.stats.Updates += updates + c.markFreshLocked(now) + c.updateStatsLocked() + c.mu.Unlock() + c.notifyChanges(notifications) + return + } var adds, removes, updates int64 notifications := make([]cacheNotification, 0, len(freshByID)) @@ -136,6 +219,8 @@ func (c *CachingStore) runReconciliation() { c.beads = freshByID c.deps = nextDeps c.dirty = make(map[string]struct{}) + c.beadSeq = make(map[string]uint64) + c.deletedSeq = make(map[string]uint64) c.syncFailures = 0 if c.state == cacheDegraded { c.state = cacheLive diff --git a/internal/beads/caching_store_reconcile_internal_test.go b/internal/beads/caching_store_reconcile_internal_test.go new file mode 100644 index 0000000000..eb39a91fb0 --- /dev/null +++ b/internal/beads/caching_store_reconcile_internal_test.go @@ -0,0 +1,203 @@ +package beads + +import ( + "context" + "encoding/json" + "sync" + "testing" +) + +type reconcileRaceStore struct { + Store + started chan struct{} + release chan struct{} + stale []Bead + + mu sync.Mutex + block bool + once sync.Once +} + +func (s *reconcileRaceStore) List(query ListQuery) ([]Bead, error) { + if !query.AllowScan { + return s.Store.List(query) + } + + s.mu.Lock() + block := s.block + s.mu.Unlock() + if !block { + return s.Store.List(query) + } + + s.once.Do(func() { + close(s.started) + }) + <-s.release + s.mu.Lock() + defer s.mu.Unlock() + return append([]Bead(nil), s.stale...), nil +} + +func TestCachingStoreReconciliationPreservesConcurrentMutation(t *testing.T) { + mem := NewMemStore() + original, err := mem.Create(Bead{Title: "before reconcile"}) + if err != nil { + t.Fatalf("Create: %v", err) + } + + backing := &reconcileRaceStore{ + Store: mem, + started: make(chan struct{}), + release: make(chan struct{}), + stale: []Bead{original}, + } + cs := NewCachingStoreForTest(backing, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + + backing.mu.Lock() + backing.block = true + backing.mu.Unlock() + + done := make(chan struct{}) + go func() { + cs.runReconciliation() + close(done) + }() + + <-backing.started + title := "after concurrent update" + if err := cs.Update(original.ID, UpdateOpts{Title: &title}); err != nil { + t.Fatalf("Update: %v", err) + } + close(backing.release) + <-done + + items, err := cs.ListOpen() + if err != nil { + t.Fatalf("ListOpen: %v", err) + } + if len(items) != 1 || items[0].Title != title { + t.Fatalf("ListOpen = %#v, want updated title %q", items, title) + } +} + +func TestCachingStoreReconciliationPreservesConcurrentEvent(t *testing.T) { + mem := NewMemStore() + original, err := mem.Create(Bead{Title: "before reconcile"}) + if err != nil { + t.Fatalf("Create: %v", err) + } + + backing := &reconcileRaceStore{ + Store: mem, + started: make(chan struct{}), + release: make(chan struct{}), + stale: []Bead{original}, + } + cs := NewCachingStoreForTest(backing, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + + backing.mu.Lock() + backing.block = true + backing.mu.Unlock() + + done := make(chan struct{}) + go func() { + cs.runReconciliation() + close(done) + }() + + <-backing.started + eventBead := cloneBead(original) + eventBead.Title = "after concurrent event" + payload, err := json.Marshal(eventBead) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + cs.ApplyEvent("bead.updated", payload) + close(backing.release) + <-done + + items, err := cs.ListOpen() + if err != nil { + t.Fatalf("ListOpen: %v", err) + } + if len(items) != 1 || items[0].Title != eventBead.Title { + t.Fatalf("ListOpen = %#v, want event title %q", items, eventBead.Title) + } +} + +func TestCachingStoreReconciliationMergesFreshDataWithConcurrentMutation(t *testing.T) { + mem := NewMemStore() + mutated, err := mem.Create(Bead{Title: "before mutate"}) + if err != nil { + t.Fatalf("Create(mutated): %v", err) + } + refreshed, err := mem.Create(Bead{Title: "before refresh"}) + if err != nil { + t.Fatalf("Create(refreshed): %v", err) + } + + backing := &reconcileRaceStore{ + Store: mem, + started: make(chan struct{}), + release: make(chan struct{}), + stale: []Bead{mutated, refreshed}, + } + cs := NewCachingStoreForTest(backing, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + + backing.mu.Lock() + backing.block = true + backing.mu.Unlock() + + done := make(chan struct{}) + go func() { + cs.runReconciliation() + close(done) + }() + + <-backing.started + title := "after concurrent update" + if err := cs.Update(mutated.ID, UpdateOpts{Title: &title}); err != nil { + t.Fatalf("Update(mutated): %v", err) + } + refreshedTitle := "after reconcile refresh" + if err := mem.Update(refreshed.ID, UpdateOpts{Title: &refreshedTitle}); err != nil { + t.Fatalf("Update(refreshed backing): %v", err) + } + refreshedBead, err := mem.Get(refreshed.ID) + if err != nil { + t.Fatalf("Get(refreshed backing): %v", err) + } + backing.mu.Lock() + backing.stale = []Bead{ + cloneBead(mutated), + cloneBead(refreshedBead), + } + backing.mu.Unlock() + close(backing.release) + <-done + + items, err := cs.ListOpen() + if err != nil { + t.Fatalf("ListOpen: %v", err) + } + gotTitles := map[string]string{} + for _, item := range items { + gotTitles[item.ID] = item.Title + } + if gotTitles[mutated.ID] != title { + t.Fatalf("mutated title = %q, want %q", gotTitles[mutated.ID], title) + } + if gotTitles[refreshed.ID] != refreshedTitle { + t.Fatalf("refreshed title = %q, want %q", gotTitles[refreshed.ID], refreshedTitle) + } +} diff --git a/internal/beads/caching_store_test.go b/internal/beads/caching_store_test.go index c64250009d..efb98c681e 100644 --- a/internal/beads/caching_store_test.go +++ b/internal/beads/caching_store_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "sync" "testing" "time" @@ -64,6 +65,592 @@ func TestCachingStoreReadThrough(t *testing.T) { } } +func TestCachingStorePrimePreservesConcurrentUpdate(t *testing.T) { + mem := beads.NewMemStore() + original, err := mem.Create(beads.Bead{Title: "before prime"}) + if err != nil { + t.Fatalf("Create: %v", err) + } + started := make(chan struct{}) + release := make(chan struct{}) + backing := &primeRaceStore{ + Store: mem, + started: started, + release: release, + stale: []beads.Bead{original}, + } + cs := beads.NewCachingStoreForTest(backing, nil) + + done := make(chan error, 1) + go func() { + done <- cs.Prime(context.Background()) + }() + + <-started + title := "after update" + if err := cs.Update(original.ID, beads.UpdateOpts{Title: &title}); err != nil { + t.Fatalf("Update: %v", err) + } + close(release) + if err := <-done; err != nil { + t.Fatalf("Prime: %v", err) + } + + got, err := cs.Get(original.ID) + if err != nil { + t.Fatalf("Get: %v", err) + } + if got.Title != title { + t.Fatalf("title after concurrent prime = %q, want %q", got.Title, title) + } +} + +func TestCachingStorePrimeDoesNotResurrectConcurrentDelete(t *testing.T) { + mem := beads.NewMemStore() + original, err := mem.Create(beads.Bead{Title: "before delete"}) + if err != nil { + t.Fatalf("Create: %v", err) + } + started := make(chan struct{}) + release := make(chan struct{}) + backing := &primeRaceStore{ + Store: mem, + started: started, + release: release, + stale: []beads.Bead{original}, + } + cs := beads.NewCachingStoreForTest(backing, nil) + + done := make(chan error, 1) + go func() { + done <- cs.Prime(context.Background()) + }() + + <-started + if err := cs.Delete(original.ID); err != nil { + t.Fatalf("Delete: %v", err) + } + close(release) + if err := <-done; err != nil { + t.Fatalf("Prime: %v", err) + } + + got, err := cs.ListOpen() + if err != nil { + t.Fatalf("ListOpen: %v", err) + } + if len(got) != 0 { + t.Fatalf("ListOpen = %#v, want deleted bead to stay absent", got) + } +} + +func TestCachingStoreCreateRefreshesSparseBead(t *testing.T) { + backing := &sparseCreateStore{Store: beads.NewMemStore()} + cs := beads.NewCachingStoreForTest(backing, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + + created, err := cs.Create(beads.Bead{ + Title: "new task", + ParentID: "parent-1", + Labels: []string{"urgent"}, + Metadata: map[string]string{"k": "v"}, + }) + if err != nil { + t.Fatalf("Create: %v", err) + } + if created.ParentID != "parent-1" { + t.Fatalf("ParentID = %q, want parent-1", created.ParentID) + } + if len(created.Labels) != 1 || created.Labels[0] != "urgent" { + t.Fatalf("Labels = %#v, want [urgent]", created.Labels) + } + if created.Metadata["k"] != "v" { + t.Fatalf("Metadata = %#v, want k=v", created.Metadata) + } +} + +func TestCachingStoreParentListUsesBackingStore(t *testing.T) { + mem := beads.NewMemStore() + parent, err := mem.Create(beads.Bead{Title: "parent"}) + if err != nil { + t.Fatalf("Create(parent): %v", err) + } + cs := beads.NewCachingStoreForTest(mem, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + child, err := mem.Create(beads.Bead{Title: "child", ParentID: parent.ID}) + if err != nil { + t.Fatalf("Create(child): %v", err) + } + + got, err := cs.List(beads.ListQuery{ParentID: parent.ID}) + if err != nil { + t.Fatalf("List(parent): %v", err) + } + if len(got) != 1 || got[0].ID != child.ID { + t.Fatalf("children = %#v, want child %s", got, child.ID) + } +} + +func TestCachingStoreParentListRefreshesCachedChildren(t *testing.T) { + mem := beads.NewMemStore() + parent, err := mem.Create(beads.Bead{Title: "parent"}) + if err != nil { + t.Fatalf("Create(parent): %v", err) + } + child, err := mem.Create(beads.Bead{Title: "child", Labels: []string{"mc-live-contract"}}) + if err != nil { + t.Fatalf("Create(child): %v", err) + } + cs := beads.NewCachingStoreForTest(mem, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + if err := mem.Update(child.ID, beads.UpdateOpts{ParentID: &parent.ID}); err != nil { + t.Fatalf("backing Update(parent): %v", err) + } + + children, err := cs.List(beads.ListQuery{ParentID: parent.ID}) + if err != nil { + t.Fatalf("List(parent): %v", err) + } + if len(children) != 1 || children[0].ParentID != parent.ID { + t.Fatalf("children = %#v, want refreshed parent %s", children, parent.ID) + } + + labeled, err := cs.List(beads.ListQuery{Label: "mc-live-contract"}) + if err != nil { + t.Fatalf("List(label): %v", err) + } + if len(labeled) != 1 || labeled[0].ParentID != parent.ID { + t.Fatalf("cached label result = %#v, want parent %s", labeled, parent.ID) + } +} + +func TestCachingStoreParentListRefreshesReparentedChildren(t *testing.T) { + mem := beads.NewMemStore() + oldParent, err := mem.Create(beads.Bead{Title: "old-parent"}) + if err != nil { + t.Fatalf("Create(old parent): %v", err) + } + newParent, err := mem.Create(beads.Bead{Title: "new-parent"}) + if err != nil { + t.Fatalf("Create(new parent): %v", err) + } + child, err := mem.Create(beads.Bead{Title: "child", ParentID: oldParent.ID, Labels: []string{"mc-live-contract"}}) + if err != nil { + t.Fatalf("Create(child): %v", err) + } + cs := beads.NewCachingStoreForTest(mem, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + + if err := mem.Update(child.ID, beads.UpdateOpts{ParentID: &newParent.ID}); err != nil { + t.Fatalf("backing Update(parent): %v", err) + } + + children, err := cs.List(beads.ListQuery{ParentID: oldParent.ID}) + if err != nil { + t.Fatalf("List(old parent): %v", err) + } + if len(children) != 0 { + t.Fatalf("old parent children = %#v, want empty after reparent", children) + } + + labeled, err := cs.List(beads.ListQuery{Label: "mc-live-contract"}) + if err != nil { + t.Fatalf("List(label): %v", err) + } + if len(labeled) != 1 || labeled[0].ParentID != newParent.ID { + t.Fatalf("cached label result = %#v, want parent %s", labeled, newParent.ID) + } +} + +func TestCachingStoreParentListPreservesConcurrentUpdate(t *testing.T) { + mem := beads.NewMemStore() + parent, err := mem.Create(beads.Bead{Title: "parent"}) + if err != nil { + t.Fatalf("Create(parent): %v", err) + } + child, err := mem.Create(beads.Bead{Title: "before", ParentID: parent.ID}) + if err != nil { + t.Fatalf("Create(child): %v", err) + } + backing := &parentListRaceStore{ + Store: mem, + parentID: parent.ID, + started: make(chan struct{}), + release: make(chan struct{}), + stale: []beads.Bead{child}, + } + cs := beads.NewCachingStoreForTest(backing, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + + childrenCh := make(chan []beads.Bead, 1) + errCh := make(chan error, 1) + go func() { + children, listErr := cs.List(beads.ListQuery{ParentID: parent.ID}) + if listErr != nil { + errCh <- listErr + return + } + childrenCh <- children + }() + + <-backing.started + title := "after concurrent update" + if err := cs.Update(child.ID, beads.UpdateOpts{Title: &title}); err != nil { + t.Fatalf("Update: %v", err) + } + close(backing.release) + + select { + case listErr := <-errCh: + t.Fatalf("List(parent): %v", listErr) + case children := <-childrenCh: + if len(children) != 1 || children[0].Title != title { + t.Fatalf("List(parent) = %#v, want updated title %q", children, title) + } + } + + got, err := cs.Get(child.ID) + if err != nil { + t.Fatalf("Get: %v", err) + } + if got.Title != title { + t.Fatalf("Get title = %q, want %q", got.Title, title) + } +} + +func TestCachingStoreParentListDoesNotResurrectConcurrentDelete(t *testing.T) { + mem := beads.NewMemStore() + parent, err := mem.Create(beads.Bead{Title: "parent"}) + if err != nil { + t.Fatalf("Create(parent): %v", err) + } + child, err := mem.Create(beads.Bead{Title: "before", ParentID: parent.ID}) + if err != nil { + t.Fatalf("Create(child): %v", err) + } + backing := &parentListRaceStore{ + Store: mem, + parentID: parent.ID, + started: make(chan struct{}), + release: make(chan struct{}), + stale: []beads.Bead{child}, + } + cs := beads.NewCachingStoreForTest(backing, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + + childrenCh := make(chan []beads.Bead, 1) + errCh := make(chan error, 1) + go func() { + children, listErr := cs.List(beads.ListQuery{ParentID: parent.ID}) + if listErr != nil { + errCh <- listErr + return + } + childrenCh <- children + }() + + <-backing.started + if err := cs.Delete(child.ID); err != nil { + t.Fatalf("Delete: %v", err) + } + close(backing.release) + + select { + case listErr := <-errCh: + t.Fatalf("List(parent): %v", listErr) + case children := <-childrenCh: + if len(children) != 0 { + t.Fatalf("List(parent) = %#v, want deleted child omitted", children) + } + } + + if _, err := cs.Get(child.ID); !errors.Is(err, beads.ErrNotFound) { + t.Fatalf("Get after delete error = %v, want ErrNotFound", err) + } +} + +func TestCachingStoreDirtyGetPreservesConcurrentEvent(t *testing.T) { + mem := beads.NewMemStore() + original, err := mem.Create(beads.Bead{Title: "before"}) + if err != nil { + t.Fatalf("Create: %v", err) + } + backing := &dirtyGetRaceStore{Store: mem} + cs := beads.NewCachingStoreForTest(backing, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + + firstTitle := "after first update" + backing.mu.Lock() + backing.failNextGet = true + backing.mu.Unlock() + if err := cs.Update(original.ID, beads.UpdateOpts{Title: &firstTitle}); err != nil { + t.Fatalf("Update(first): %v", err) + } + + stale, err := mem.Get(original.ID) + if err != nil { + t.Fatalf("Get(backing stale): %v", err) + } + backing.mu.Lock() + backing.stale = stale + backing.started = make(chan struct{}) + backing.release = make(chan struct{}) + backing.blockNextGet = true + backing.mu.Unlock() + + gotCh := make(chan beads.Bead, 1) + errCh := make(chan error, 1) + go func() { + got, getErr := cs.Get(original.ID) + if getErr != nil { + errCh <- getErr + return + } + gotCh <- got + }() + + <-backing.started + secondTitle := "after concurrent event" + if err := mem.Update(original.ID, beads.UpdateOpts{Title: &secondTitle}); err != nil { + t.Fatalf("Update(backing second): %v", err) + } + eventBead, err := mem.Get(original.ID) + if err != nil { + t.Fatalf("Get(backing second): %v", err) + } + payload, err := json.Marshal(eventBead) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + cs.ApplyEvent("bead.updated", payload) + close(backing.release) + + select { + case getErr := <-errCh: + t.Fatalf("Get: %v", getErr) + case got := <-gotCh: + if got.Title != secondTitle { + t.Fatalf("Get title = %q, want %q", got.Title, secondTitle) + } + } + + got, err := cs.Get(original.ID) + if err != nil { + t.Fatalf("Get cached: %v", err) + } + if got.Title != secondTitle { + t.Fatalf("cached title = %q, want %q", got.Title, secondTitle) + } +} + +func TestCachingStoreUpdateReflectsWriteIntentWhenImmediateReadIsStale(t *testing.T) { + mem := beads.NewMemStore() + original, err := mem.Create(beads.Bead{ + Title: "root", + Labels: []string{"root", "needs-update"}, + Metadata: map[string]string{"mc.contract.run_id": "r1"}, + }) + if err != nil { + t.Fatalf("Create: %v", err) + } + backing := &staleReadAfterUpdateStore{ + Store: mem, + stale: original, + } + cs := beads.NewCachingStoreForTest(backing, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + + status := "in_progress" + if err := cs.Update(original.ID, beads.UpdateOpts{ + Status: &status, + Labels: []string{"verified"}, + RemoveLabels: []string{"needs-update"}, + Metadata: map[string]string{ + "mc.contract.metadata_update": "true", + }, + }); err != nil { + t.Fatalf("Update: %v", err) + } + + got, err := cs.Get(original.ID) + if err != nil { + t.Fatalf("Get: %v", err) + } + if got.Status != "in_progress" { + t.Fatalf("status = %q, want in_progress", got.Status) + } + if got.Metadata["mc.contract.metadata_update"] != "true" || got.Metadata["mc.contract.run_id"] != "r1" { + t.Fatalf("metadata = %#v, want original plus update", got.Metadata) + } + if !containsString(got.Labels, "verified") || containsString(got.Labels, "needs-update") { + t.Fatalf("labels = %#v, want verified without needs-update", got.Labels) + } +} + +func TestCachingStoreUpdateDoesNotDuplicateAuthoritativeLabels(t *testing.T) { + mem := beads.NewMemStore() + original, err := mem.Create(beads.Bead{ + Title: "root", + Labels: []string{"root"}, + }) + if err != nil { + t.Fatalf("Create: %v", err) + } + cs := beads.NewCachingStoreForTest(mem, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + + if err := cs.Update(original.ID, beads.UpdateOpts{Labels: []string{"verified"}}); err != nil { + t.Fatalf("Update: %v", err) + } + + got, err := cs.ListOpen() + if err != nil { + t.Fatalf("ListOpen: %v", err) + } + if len(got) != 1 { + t.Fatalf("ListOpen returned %d beads, want 1", len(got)) + } + verifiedCount := 0 + for _, label := range got[0].Labels { + if label == "verified" { + verifiedCount++ + } + } + if verifiedCount != 1 { + t.Fatalf("labels = %#v, want exactly one verified label", got[0].Labels) + } +} + +type staleReadAfterUpdateStore struct { + beads.Store + mu sync.Mutex + stale beads.Bead + returnOld bool +} + +type sparseCreateStore struct { + beads.Store +} + +func (s *sparseCreateStore) Create(b beads.Bead) (beads.Bead, error) { + created, err := s.Store.Create(b) + if err != nil { + return beads.Bead{}, err + } + return beads.Bead{ID: created.ID, Title: created.Title}, nil +} + +func (s *staleReadAfterUpdateStore) Update(id string, opts beads.UpdateOpts) error { + if err := s.Store.Update(id, opts); err != nil { + return err + } + s.mu.Lock() + s.returnOld = true + s.mu.Unlock() + return nil +} + +func (s *staleReadAfterUpdateStore) Get(id string) (beads.Bead, error) { + s.mu.Lock() + if s.returnOld && id == s.stale.ID { + s.returnOld = false + stale := s.stale + s.mu.Unlock() + return stale, nil + } + s.mu.Unlock() + return s.Store.Get(id) +} + +type primeRaceStore struct { + beads.Store + started chan struct{} + release chan struct{} + stale []beads.Bead + once sync.Once +} + +func (s *primeRaceStore) List(query beads.ListQuery) ([]beads.Bead, error) { + if !query.AllowScan { + return s.Store.List(query) + } + s.once.Do(func() { + close(s.started) + }) + <-s.release + return append([]beads.Bead(nil), s.stale...), nil +} + +type parentListRaceStore struct { + beads.Store + parentID string + started chan struct{} + release chan struct{} + stale []beads.Bead + once sync.Once +} + +func (s *parentListRaceStore) List(query beads.ListQuery) ([]beads.Bead, error) { + if query.ParentID != s.parentID { + return s.Store.List(query) + } + s.once.Do(func() { + close(s.started) + }) + <-s.release + return append([]beads.Bead(nil), s.stale...), nil +} + +type dirtyGetRaceStore struct { + beads.Store + mu sync.Mutex + failNextGet bool + blockNextGet bool + started chan struct{} + release chan struct{} + stale beads.Bead +} + +func (s *dirtyGetRaceStore) Get(id string) (beads.Bead, error) { + s.mu.Lock() + switch { + case s.failNextGet: + s.failNextGet = false + s.mu.Unlock() + return beads.Bead{}, errors.New("transient get failure") + case s.blockNextGet && id == s.stale.ID: + s.blockNextGet = false + started := s.started + release := s.release + stale := s.stale + s.mu.Unlock() + close(started) + <-release + return stale, nil + default: + s.mu.Unlock() + return s.Store.Get(id) + } +} + func TestCachingStoreGetFallsBackForClosedBeadsAfterPrime(t *testing.T) { t.Parallel() mem := beads.NewMemStore() @@ -255,6 +842,45 @@ func TestCachingStoreWriteThrough(t *testing.T) { } } +func TestCachingStoreCloseNotifiesWhenBeadIsMissingFromCache(t *testing.T) { + t.Parallel() + mem := beads.NewMemStore() + cs := beads.NewCachingStoreForTest(mem, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + created, err := mem.Create(beads.Bead{Title: "external"}) + if err != nil { + t.Fatalf("Create: %v", err) + } + + var events []string + cs = beads.NewCachingStoreForTest(mem, func(eventType, beadID string, payload json.RawMessage) { + var b beads.Bead + if err := json.Unmarshal(payload, &b); err != nil { + t.Fatalf("unmarshal callback payload: %v", err) + } + events = append(events, eventType+":"+beadID+":"+b.Status) + }) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime after callback install: %v", err) + } + if err := cs.Delete(created.ID); err != nil { + t.Fatalf("Delete setup: %v", err) + } + created, err = mem.Create(beads.Bead{Title: "external"}) + if err != nil { + t.Fatalf("Create second: %v", err) + } + + if err := cs.Close(created.ID); err != nil { + t.Fatalf("Close: %v", err) + } + if len(events) != 1 || events[0] != "bead.closed:"+created.ID+":closed" { + t.Fatalf("events = %#v, want bead.closed for missing cached bead", events) + } +} + func TestCachingStoreApplyEvent(t *testing.T) { t.Parallel() mem := beads.NewMemStore() @@ -270,20 +896,24 @@ func TestCachingStoreApplyEvent(t *testing.T) { payload, _ := json.Marshal(newBead) cs.ApplyEvent("bead.created", payload) - got, err := cs.Get("ext-1") - if err != nil { - t.Fatalf("Get after ApplyEvent create: %v", err) - } + got := requireCachedBead(t, cs, "ext-1", false) if got.Title != "External" { t.Fatalf("title = %q, want External", got.Title) } // Apply an update event. - updated := beads.Bead{ID: b1.ID, Title: "Modified by agent", Status: "open", Metadata: map[string]string{"gc.step_ref": "mol.review"}} + updatedTitle := "Modified by agent" + if err := mem.Update(b1.ID, beads.UpdateOpts{ + Title: &updatedTitle, + Metadata: map[string]string{"gc.step_ref": "mol.review"}, + }); err != nil { + t.Fatalf("Update backing: %v", err) + } + updated := beads.Bead{ID: b1.ID, Title: updatedTitle, Status: "open", Metadata: map[string]string{"gc.step_ref": "mol.review"}} payload, _ = json.Marshal(updated) cs.ApplyEvent("bead.updated", payload) - got, _ = cs.Get(b1.ID) + got = requireCachedBead(t, cs, b1.ID, false) if got.Title != "Modified by agent" { t.Fatalf("title after update event = %q, want Modified by agent", got.Title) } @@ -292,9 +922,20 @@ func TestCachingStoreApplyEvent(t *testing.T) { } // Apply a close event with the full closed bead payload. + closedTitle := "Closed by agent" + if err := mem.Update(b1.ID, beads.UpdateOpts{ + Title: &closedTitle, + Labels: []string{"done"}, + Metadata: map[string]string{"gc.outcome": "pass"}, + }); err != nil { + t.Fatalf("Update backing before close: %v", err) + } + if err := mem.Close(b1.ID); err != nil { + t.Fatalf("Close backing: %v", err) + } closed := beads.Bead{ ID: b1.ID, - Title: "Closed by agent", + Title: closedTitle, Status: "closed", Labels: []string{"done"}, Metadata: map[string]string{"gc.outcome": "pass"}, @@ -302,7 +943,7 @@ func TestCachingStoreApplyEvent(t *testing.T) { payload, _ = json.Marshal(closed) cs.ApplyEvent("bead.closed", payload) - got, _ = cs.Get(b1.ID) + got = requireCachedBead(t, cs, b1.ID, true) if got.Status != "closed" { t.Fatalf("status after close event = %q, want closed", got.Status) } @@ -320,13 +961,80 @@ func TestCachingStoreApplyEvent(t *testing.T) { } } -func TestCachingStoreApplyEventCoercesNonStringMetadata(t *testing.T) { +func TestCachingStoreApplyEventRefreshesPartialHookPayload(t *testing.T) { t.Parallel() mem := beads.NewMemStore() - b1, err := mem.Create(beads.Bead{Title: "Existing"}) + parent, err := mem.Create(beads.Bead{Title: "parent"}) if err != nil { - t.Fatalf("Create: %v", err) + t.Fatalf("Create parent: %v", err) + } + child, err := mem.Create(beads.Bead{ + Title: "child", + ParentID: parent.ID, + Labels: []string{"mc-live-contract"}, + }) + if err != nil { + t.Fatalf("Create child: %v", err) + } + + backing := &eventGetFailStore{Store: mem} + cs := beads.NewCachingStoreForTest(backing, nil) + if err := cs.Prime(context.Background()); err != nil { + t.Fatalf("Prime: %v", err) + } + backing.failGet = true + + updatedTitle := "child updated externally" + if err := mem.Update(child.ID, beads.UpdateOpts{Title: &updatedTitle}); err != nil { + t.Fatalf("Update backing: %v", err) } + payload, err := json.Marshal(map[string]any{ + "id": child.ID, + "title": updatedTitle, + "status": "open", + "issue_type": "task", + "owner": "agent@example.com", + "updated_at": "2026-04-25T04:45:55Z", + }) + if err != nil { + t.Fatalf("marshal payload: %v", err) + } + + cs.ApplyEvent("bead.updated", payload) + if stats := cs.Stats(); stats.ProblemCount != 0 { + t.Fatalf("ProblemCount = %d, want 0 (last problem: %s)", stats.ProblemCount, stats.LastProblem) + } + + labeled, err := cs.List(beads.ListQuery{Label: "mc-live-contract"}) + if err != nil { + t.Fatalf("List(label): %v", err) + } + if len(labeled) != 1 || labeled[0].ID != child.ID { + t.Fatalf("labeled = %#v, want child %s", labeled, child.ID) + } + if labeled[0].ParentID != parent.ID { + t.Fatalf("ParentID = %q, want %q", labeled[0].ParentID, parent.ID) + } + if labeled[0].Title != updatedTitle { + t.Fatalf("Title = %q, want %q", labeled[0].Title, updatedTitle) + } +} + +type eventGetFailStore struct { + beads.Store + failGet bool +} + +func (s *eventGetFailStore) Get(id string) (beads.Bead, error) { + if s.failGet { + return beads.Bead{}, errors.New("unexpected event backing get") + } + return s.Store.Get(id) +} + +func TestCachingStoreApplyEventCoercesNonStringMetadata(t *testing.T) { + t.Parallel() + mem := beads.NewMemStore() cs := beads.NewCachingStoreForTest(mem, nil) if err := cs.Prime(context.Background()); err != nil { @@ -334,7 +1042,7 @@ func TestCachingStoreApplyEventCoercesNonStringMetadata(t *testing.T) { } payload, err := json.Marshal(map[string]any{ - "id": b1.ID, + "id": "ext-1", "title": "mayor", "status": "open", "issue_type": "session", @@ -356,10 +1064,7 @@ func TestCachingStoreApplyEventCoercesNonStringMetadata(t *testing.T) { t.Fatalf("ProblemCount = %d, want 0 (last problem: %s)", stats.ProblemCount, stats.LastProblem) } - got, err := cs.Get(b1.ID) - if err != nil { - t.Fatalf("Get after ApplyEvent update: %v", err) - } + got := requireCachedBead(t, cs, "ext-1", false) if got.Type != "session" { t.Fatalf("Type = %q, want session", got.Type) } @@ -374,6 +1079,21 @@ func TestCachingStoreApplyEventCoercesNonStringMetadata(t *testing.T) { } } +func requireCachedBead(t *testing.T, cs *beads.CachingStore, id string, includeClosed bool) beads.Bead { + t.Helper() + items, err := cs.List(beads.ListQuery{AllowScan: true, IncludeClosed: includeClosed}) + if err != nil { + t.Fatalf("List cached beads: %v", err) + } + for _, item := range items { + if item.ID == id { + return item + } + } + t.Fatalf("cached bead %q missing from %#v", id, items) + return beads.Bead{} +} + func TestCachingStoreApplyEventIgnoredWhenDegraded(t *testing.T) { t.Parallel() mem := beads.NewMemStore() @@ -512,3 +1232,12 @@ func (s *failingIncludeClosedMetadataStore) List(query beads.ListQuery) ([]beads } func strPtr(s string) *string { return &s } + +func containsString(values []string, want string) bool { + for _, value := range values { + if value == want { + return true + } + } + return false +} diff --git a/internal/beads/caching_store_writes.go b/internal/beads/caching_store_writes.go index 7d479ca865..d86409660e 100644 --- a/internal/beads/caching_store_writes.go +++ b/internal/beads/caching_store_writes.go @@ -13,9 +13,17 @@ func (c *CachingStore) Create(b Bead) (Bead, error) { return created, err } + if fresh, err := c.backing.Get(created.ID); err == nil { + created = fresh + } else if !errors.Is(err, ErrNotFound) { + c.recordProblem("refresh bead after create", fmt.Errorf("%s: %w", created.ID, err)) + } + c.mu.Lock() + c.noteMutationLocked(created.ID) c.beads[created.ID] = cloneBead(created) delete(c.dirty, created.ID) + delete(c.deletedSeq, created.ID) c.markFreshLocked(time.Now()) c.updateStatsLocked() c.mu.Unlock() @@ -39,10 +47,13 @@ func (c *CachingStore) Update(id string, opts UpdateOpts) error { c.recordProblem("refresh bead after update", fmt.Errorf("%s: %w", id, err)) return nil } + fresh = applyUpdateOptsToBead(fresh, opts) c.mu.Lock() + c.noteMutationLocked(id) c.beads[id] = cloneBead(fresh) delete(c.dirty, id) + delete(c.deletedSeq, id) c.markFreshLocked(time.Now()) c.updateStatsLocked() c.mu.Unlock() @@ -59,15 +70,31 @@ func (c *CachingStore) Close(id string) error { var closed Bead var found bool + if fresh, err := c.backing.Get(id); err == nil { + closed = fresh + closed.Status = "closed" + found = true + } else if !errors.Is(err, ErrNotFound) { + c.recordProblem("refresh bead after close", fmt.Errorf("%s: %w", id, err)) + } + c.mu.Lock() + c.noteMutationLocked(id) if b, ok := c.beads[id]; ok { b.Status = "closed" c.beads[id] = b delete(c.dirty, id) + delete(c.deletedSeq, id) closed = cloneBead(b) found = true c.markFreshLocked(time.Now()) c.updateStatsLocked() + } else if found { + c.beads[id] = cloneBead(closed) + delete(c.dirty, id) + delete(c.deletedSeq, id) + c.markFreshLocked(time.Now()) + c.updateStatsLocked() } c.mu.Unlock() @@ -103,6 +130,7 @@ func (c *CachingStore) CloseAll(ids []string, metadata map[string]string) (int, notifications := make([]cacheNotification, 0, len(refreshed)) c.mu.Lock() + c.noteMutationLocked(ids...) if refreshErr != nil { c.recordProblemLocked("close-all refresh", refreshErr) } @@ -113,6 +141,7 @@ func (c *CachingStore) CloseAll(ids []string, metadata map[string]string) (int, previous, hadPrevious := c.beads[item.id] c.beads[item.id] = cloneBead(item.bead) delete(c.dirty, item.id) + delete(c.deletedSeq, item.id) if item.bead.Status == "closed" { delete(c.deps, item.id) } @@ -137,6 +166,7 @@ func (c *CachingStore) SetMetadata(id, key, value string) error { } c.mu.Lock() + c.noteMutationLocked(id) if b, ok := c.beads[id]; ok { if b.Metadata == nil { b.Metadata = make(map[string]string) @@ -144,6 +174,7 @@ func (c *CachingStore) SetMetadata(id, key, value string) error { b.Metadata[key] = value c.beads[id] = b delete(c.dirty, id) + delete(c.deletedSeq, id) } c.markFreshLocked(time.Now()) c.updateStatsLocked() @@ -158,6 +189,7 @@ func (c *CachingStore) SetMetadataBatch(id string, kvs map[string]string) error } c.mu.Lock() + c.noteMutationLocked(id) if b, ok := c.beads[id]; ok { if b.Metadata == nil { b.Metadata = make(map[string]string, len(kvs)) @@ -167,6 +199,7 @@ func (c *CachingStore) SetMetadataBatch(id string, kvs map[string]string) error } c.beads[id] = b delete(c.dirty, id) + delete(c.deletedSeq, id) } c.markFreshLocked(time.Now()) c.updateStatsLocked() @@ -181,12 +214,14 @@ func (c *CachingStore) DepAdd(issueID, dependsOnID, depType string) error { } c.mu.Lock() + c.noteMutationLocked(issueID) deps := c.deps[issueID] for i, d := range deps { if d.DependsOnID == dependsOnID { deps[i].Type = depType c.deps[issueID] = deps delete(c.dirty, issueID) + delete(c.deletedSeq, issueID) c.markFreshLocked(time.Now()) c.updateStatsLocked() c.mu.Unlock() @@ -195,6 +230,7 @@ func (c *CachingStore) DepAdd(issueID, dependsOnID, depType string) error { } c.deps[issueID] = append(deps, Dep{IssueID: issueID, DependsOnID: dependsOnID, Type: depType}) delete(c.dirty, issueID) + delete(c.deletedSeq, issueID) c.markFreshLocked(time.Now()) c.updateStatsLocked() c.mu.Unlock() @@ -208,11 +244,13 @@ func (c *CachingStore) DepRemove(issueID, dependsOnID string) error { } c.mu.Lock() + c.noteMutationLocked(issueID) deps := c.deps[issueID] for i, d := range deps { if d.DependsOnID == dependsOnID { c.deps[issueID] = append(deps[:i], deps[i+1:]...) delete(c.dirty, issueID) + delete(c.deletedSeq, issueID) break } } @@ -229,11 +267,77 @@ func (c *CachingStore) Delete(id string) error { } c.mu.Lock() + seq := c.noteMutationLocked(id) delete(c.beads, id) delete(c.deps, id) delete(c.dirty, id) + delete(c.beadSeq, id) + c.deletedSeq[id] = seq c.markFreshLocked(time.Now()) c.updateStatsLocked() c.mu.Unlock() return nil } + +func applyUpdateOptsToBead(bead Bead, opts UpdateOpts) Bead { + if opts.Title != nil { + bead.Title = *opts.Title + } + if opts.Status != nil { + bead.Status = *opts.Status + } + if opts.Type != nil { + bead.Type = *opts.Type + } + if opts.Priority != nil { + bead.Priority = cloneIntPtr(opts.Priority) + } + if opts.Description != nil { + bead.Description = *opts.Description + } + if opts.ParentID != nil { + bead.ParentID = *opts.ParentID + } + if opts.Assignee != nil { + bead.Assignee = *opts.Assignee + } + if len(opts.Metadata) > 0 { + if bead.Metadata == nil { + bead.Metadata = make(map[string]string, len(opts.Metadata)) + } + for key, value := range opts.Metadata { + bead.Metadata[key] = value + } + } + if len(opts.Labels) > 0 || len(opts.RemoveLabels) > 0 { + remove := make(map[string]struct{}, len(opts.RemoveLabels)) + for _, label := range opts.RemoveLabels { + remove[label] = struct{}{} + } + + labels := make([]string, 0, len(bead.Labels)+len(opts.Labels)) + seen := make(map[string]struct{}, len(bead.Labels)+len(opts.Labels)) + for _, label := range bead.Labels { + if _, drop := remove[label]; drop { + continue + } + if _, exists := seen[label]; exists { + continue + } + labels = append(labels, label) + seen[label] = struct{}{} + } + for _, label := range opts.Labels { + if _, drop := remove[label]; drop { + continue + } + if _, exists := seen[label]; exists { + continue + } + labels = append(labels, label) + seen[label] = struct{}{} + } + bead.Labels = labels + } + return bead +} diff --git a/internal/cityinit/cityinit.go b/internal/cityinit/cityinit.go new file mode 100644 index 0000000000..903dc68df7 --- /dev/null +++ b/internal/cityinit/cityinit.go @@ -0,0 +1,210 @@ +// Package cityinit is the domain contract for city scaffolding and +// finalization. It defines the typed request, result, and sentinel +// errors that both projections — the CLI (cmd/gc/cmd_init.go) and the +// HTTP API (internal/api/huma_handlers_supervisor.go:humaHandleCityCreate) — +// use when creating a new city. +// +// The Initializer interface is implemented in cmd/gc (where the +// scaffold + finalize body currently lives) and injected into the +// HTTP supervisor at construction. The HTTP handler calls +// Initializer.Init in-process; there is no subprocess, no +// 30-second deadline, no stderr-scraping for error dispatch. +// +// A follow-up refactor will physically move the scaffold/finalize +// body into this package so the domain logic lives in internal/ +// (per engdocs/architecture/api-control-plane.md §1). Until then, injecting the +// implementation from cmd/gc at startup preserves the architectural +// intent that "the CLI and the HTTP API are projections over the +// shared object model" — both surfaces drive the same code path via +// the same typed contract. +package cityinit + +import ( + "context" + "errors" +) + +// Typed sentinel errors. Both projections map them to their own +// surface: the CLI renders human-readable blocker lists; the HTTP +// handler maps each to the appropriate status code (409, 400, 503, +// etc.). Error strings are suitable for display in either surface. +var ( + // ErrAlreadyInitialized indicates the target directory already + // contains a Gas City scaffold. The HTTP API maps this to 409 + // Conflict. The CLI can either ignore (idempotent reinit) or + // surface, depending on flags. + ErrAlreadyInitialized = errors.New("city already initialized") + + // ErrInvalidProvider indicates an unknown builtin provider. The + // HTTP API maps this to 400 Bad Request (or 422 Unprocessable + // Entity at the typed-input layer). + ErrInvalidProvider = errors.New("invalid provider") + + // ErrInvalidBootstrapProfile indicates an unrecognized + // bootstrap_profile value. + ErrInvalidBootstrapProfile = errors.New("invalid bootstrap profile") + + // ErrMissingDependency indicates a hard runtime dependency + // (tmux, git, dolt, bd, flock, jq, pgrep, lsof) is missing or + // too old. Maps to 503 Service Unavailable at the HTTP layer. + // The error message lists every missing dependency so the CLI + // can render install hints without another probe pass. + ErrMissingDependency = errors.New("missing hard dependency") + + // ErrProviderNotReady indicates at least one provider the city + // references is not ready (no auth, not installed, invalid + // config, or probe failure). Only returned when + // InitRequest.SkipProviderReadiness is false. Maps to 503 at + // the HTTP layer. + ErrProviderNotReady = errors.New("provider not ready") + + // ErrConfigLoad indicates the city was scaffolded but its + // on-disk configuration could not be re-parsed after write. + // Usually a bug in the scaffold step; maps to 500. + ErrConfigLoad = errors.New("loading city config") + + // ErrNotWired indicates the HTTP handler was called before a + // concrete Initializer was injected into the supervisor. This + // is a programmer-bug tripwire: every SupervisorMux constructed + // at runtime must have a non-nil Initializer. + ErrNotWired = errors.New("cityinit: no Initializer wired into supervisor") + + // ErrNotRegistered indicates Unregister was called for a city + // that is not in the supervisor registry. Maps to 404 Not Found + // at the HTTP layer. + ErrNotRegistered = errors.New("city not registered with supervisor") +) + +// InitRequest is the typed input. Both projections populate it from +// their own surface (CLI flags, HTTP request body) and hand it to +// Initializer.Init; neither duplicates validation or logic. +type InitRequest struct { + // Dir is the absolute path of the new city directory. Callers + // resolve relative paths before invoking Init (the CLI uses + // filepath.Abs; the API handler joins against $HOME when + // relative). + Dir string + + // Provider is the builtin provider key ("claude", "codex", + // "gemini", ...) for the city's default workspace. Empty iff + // StartCommand is set. + Provider string + + // StartCommand is an opt-in custom workspace provider command. + // Empty unless the caller wants a non-builtin workspace. + StartCommand string + + // BootstrapProfile is one of "", "k8s-cell", "kubernetes", + // "kubernetes-cell", "single-host-compat". + BootstrapProfile string + + // NameOverride is an explicit city name. Empty means derive + // from filepath.Base(Dir). + NameOverride string + + // SkipProviderReadiness skips the provider-auth preflight when + // true. The async HTTP create handler defaults to true and + // surfaces dependency/provider blockers later via the terminal + // city.init_failed event on /v0/events/stream. The CLI defaults + // to false so first-time users see auth-needed errors + // immediately. + SkipProviderReadiness bool + + // ConfigName selects the scaffold template. One of "tutorial" + // (default), "gastown", or "custom". Empty is treated as + // "tutorial". The CLI wizard resolves this; the HTTP API + // always leaves it empty. + ConfigName string +} + +// InitResult describes what Init produced. Callers build their own +// surface-specific response from it (CLI status messages, HTTP JSON +// body). +type InitResult struct { + // CityName is the name persisted to city.toml. + CityName string + + // CityPath is the absolute city directory (same as + // InitRequest.Dir after normalization). + CityPath string + + // ProviderUsed is the resolved provider name. + ProviderUsed string + + // Resumed is true when Init detected an existing scaffold and + // skipped to finalization only. + Resumed bool +} + +// Initializer is the domain contract for city lifecycle on the +// supervisor: scaffolding, finalization, and unregistration. Exactly +// one implementation exists per process, supplied at supervisor +// construction (see internal/api.NewSupervisorMux). Both projections +// — CLI and HTTP API — drive the same code path via this interface. +type Initializer interface { + // Init scaffolds + finalizes a new city. + // + // Preconditions: req.Dir is an absolute path; exactly one of + // req.Provider / req.StartCommand is set; req.BootstrapProfile + // is a known value. + // + // Postconditions on nil error: the directory contains a + // complete city scaffold; the bead provider is initialized; the + // city is registered with any running supervisor. + // + // Errors returned wrap one of the ErrXxx sentinels in this + // package so callers can dispatch via errors.Is. + Init(ctx context.Context, req InitRequest) (*InitResult, error) + + // Scaffold writes the new city's on-disk shape and registers it + // with the supervisor — the fast portion of Init. Used by the + // HTTP API handler behind POST /v0/city so it can return 202 + // Accepted immediately instead of blocking on the slow finalize + // work. The supervisor reconciler takes over from there; city + // readiness is signaled via city.ready / city.init_failed + // events on the supervisor event bus, not via the handler's + // response body. + // + // The implementation emits a city.created event before + // returning so subscribers of /v0/events/stream observe the + // new city before Finalize begins. + Scaffold(ctx context.Context, req InitRequest) (*InitResult, error) + + // Unregister removes the city from the supervisor's registry + // and signals the supervisor to reconcile. Used by the HTTP API + // handler behind POST /v0/city/{cityName}/unregister so it can + // return 202 Accepted immediately while the reconciler stops + // the controller asynchronously. + // + // Returns ErrNotRegistered if the named city is not in the + // registry. On nil error, emits a city.unregister_requested + // event to the city's event log so subscribers of + // /v0/events/stream observe the start of the teardown. The + // terminal completion event (city.unregistered or + // city.unregister_failed) is emitted by the supervisor + // reconciler once the city's controller finishes stopping. + // + // The city directory itself is NOT touched. Users that want to + // purge the directory remove it separately. + Unregister(ctx context.Context, req UnregisterRequest) (*UnregisterResult, error) +} + +// UnregisterRequest is the typed input for Initializer.Unregister. +type UnregisterRequest struct { + // CityName is the supervisor-registered name (effective name, + // e.g. workspace.name from city.toml, or directory basename if + // unset). Required; looked up in the registry by name. + CityName string +} + +// UnregisterResult describes what Unregister produced. Callers +// build their own surface-specific response from it. +type UnregisterResult struct { + // CityName is the resolved registry name. + CityName string + + // CityPath is the absolute city directory whose entry was + // removed from the registry. Useful for clients that want to + // filter completion events by path as well as name. + CityPath string +} diff --git a/internal/config/chain.go b/internal/config/chain.go index a7a74238f0..69985d519b 100644 --- a/internal/config/chain.go +++ b/internal/config/chain.go @@ -385,6 +385,8 @@ func recordScalarProvenance(spec ProviderSpec, layer string, into map[string]str set("resume_style", spec.ResumeStyle) set("resume_command", spec.ResumeCommand) set("session_id_flag", spec.SessionIDFlag) + set("acp_command", spec.ACPCommand) + setSlice("acp_args", spec.ACPArgs) set("title_model", spec.TitleModel) set("options_schema_merge", spec.OptionsSchemaMerge) setSlice("print_args", spec.PrintArgs) diff --git a/internal/config/launch_command.go b/internal/config/launch_command.go index 3cbab9bbc3..c9444d6a6d 100644 --- a/internal/config/launch_command.go +++ b/internal/config/launch_command.go @@ -22,12 +22,16 @@ type ProviderLaunchCommand struct { // for session startup. It starts from the raw provider command, applies // schema-managed defaults plus any explicit option overrides, and appends a // provider-owned settings file when present. -func BuildProviderLaunchCommand(cityPath string, resolved *ResolvedProvider, optionOverrides map[string]string) (ProviderLaunchCommand, error) { +// +// When transport is "acp", the ACP-specific command (ACPCommand/ACPArgs) is +// used as the base instead of the default Command/Args. Pass "" for the +// default (tmux) transport. +func BuildProviderLaunchCommand(cityPath string, resolved *ResolvedProvider, optionOverrides map[string]string, transport string) (ProviderLaunchCommand, error) { if resolved == nil { return ProviderLaunchCommand{}, fmt.Errorf("resolved provider is nil") } - command := resolved.CommandString() + command := providerLaunchBaseCommand(resolved, transport) if len(resolved.OptionsSchema) > 0 { mergedOptions := make(map[string]string, len(resolved.EffectiveDefaults)+len(optionOverrides)) for key, value := range resolved.EffectiveDefaults { @@ -48,7 +52,34 @@ func BuildProviderLaunchCommand(cityPath string, resolved *ResolvedProvider, opt } } - settingsPath, settingsRel := ProviderSettingsSource(cityPath, resolved.Name) + return appendProviderSettings(cityPath, resolved.Name, command), nil +} + +// BuildProviderLaunchCommandWithoutOptions composes the transport-specific +// provider command plus any provider-owned settings file without applying +// schema-managed defaults or explicit option overrides. +// +// Deferred agent-session creation uses this helper because option state is +// stored separately in template_overrides and applied later at actual start +// time, but the stored base command must still match the selected transport +// and provider-owned settings semantics. +func BuildProviderLaunchCommandWithoutOptions(cityPath string, resolved *ResolvedProvider, transport string) (ProviderLaunchCommand, error) { + if resolved == nil { + return ProviderLaunchCommand{}, fmt.Errorf("resolved provider is nil") + } + return appendProviderSettings(cityPath, resolved.Name, providerLaunchBaseCommand(resolved, transport)), nil +} + +func providerLaunchBaseCommand(resolved *ResolvedProvider, transport string) string { + command := resolved.CommandString() + if transport == "acp" { + command = resolved.ACPCommandString() + } + return command +} + +func appendProviderSettings(cityPath, providerName, command string) ProviderLaunchCommand { + settingsPath, settingsRel := ProviderSettingsSource(cityPath, providerName) if settingsPath != "" { command = command + " " + fmt.Sprintf("--settings %q", settingsPath) } @@ -57,7 +88,7 @@ func BuildProviderLaunchCommand(cityPath string, resolved *ResolvedProvider, opt Command: command, SettingsPath: settingsPath, SettingsRel: settingsRel, - }, nil + } } // ProviderSettingsSource returns the provider-owned settings file that should diff --git a/internal/config/launch_command_test.go b/internal/config/launch_command_test.go index 39b4c0256d..93fe87f260 100644 --- a/internal/config/launch_command_test.go +++ b/internal/config/launch_command_test.go @@ -20,7 +20,7 @@ func TestBuildProviderLaunchCommandAddsDefaultsAndSettings(t *testing.T) { spec := BuiltinProviders()["claude"] rp := specToResolved("claude", &spec) - got, err := BuildProviderLaunchCommand(dir, rp, nil) + got, err := BuildProviderLaunchCommand(dir, rp, nil, "") if err != nil { t.Fatalf("BuildProviderLaunchCommand: %v", err) } @@ -44,7 +44,7 @@ func TestBuildProviderLaunchCommandAppliesOptionOverrides(t *testing.T) { got, err := BuildProviderLaunchCommand("", rp, map[string]string{ "permission_mode": "plan", "effort": "low", - }) + }, "") if err != nil { t.Fatalf("BuildProviderLaunchCommand: %v", err) } @@ -65,7 +65,7 @@ func TestBuildProviderLaunchCommandIgnoresInitialMessageOverride(t *testing.T) { got, err := BuildProviderLaunchCommand("", rp, map[string]string{ "initial_message": "hello", "effort": "low", - }) + }, "") if err != nil { t.Fatalf("BuildProviderLaunchCommand: %v", err) } @@ -75,3 +75,62 @@ func TestBuildProviderLaunchCommandIgnoresInitialMessageOverride(t *testing.T) { t.Fatalf("Command = %q, want %q", got.Command, want) } } + +func TestBuildProviderLaunchCommandUsesACPCommand(t *testing.T) { + rp := &ResolvedProvider{ + Command: "custom-opencode", + ACPArgs: []string{"acp"}, + } + + t.Run("acp transport uses ACPCommandString", func(t *testing.T) { + got, err := BuildProviderLaunchCommand("", rp, nil, "acp") + if err != nil { + t.Fatalf("BuildProviderLaunchCommand: %v", err) + } + want := "custom-opencode acp" + if got.Command != want { + t.Fatalf("Command = %q, want %q", got.Command, want) + } + }) + + t.Run("default transport uses CommandString", func(t *testing.T) { + got, err := BuildProviderLaunchCommand("", rp, nil, "") + if err != nil { + t.Fatalf("BuildProviderLaunchCommand: %v", err) + } + want := "custom-opencode" + if got.Command != want { + t.Fatalf("Command = %q, want %q", got.Command, want) + } + }) +} + +func TestBuildProviderLaunchCommandWithoutOptionsSkipsDefaultsButKeepsSettings(t *testing.T) { + dir := t.TempDir() + runtimeDir := filepath.Join(dir, ".gc") + if err := os.MkdirAll(runtimeDir, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(runtimeDir, "settings.json"), []byte(`{}`), 0o644); err != nil { + t.Fatal(err) + } + + spec := BuiltinProviders()["claude"] + rp := specToResolved("claude", &spec) + + got, err := BuildProviderLaunchCommandWithoutOptions(dir, rp, "") + if err != nil { + t.Fatalf("BuildProviderLaunchCommandWithoutOptions: %v", err) + } + + wantCommand := fmt.Sprintf("claude --settings %q", filepath.Join(dir, ".gc", "settings.json")) + if got.Command != wantCommand { + t.Fatalf("Command = %q, want %q", got.Command, wantCommand) + } + if got.SettingsPath != filepath.Join(dir, ".gc", "settings.json") { + t.Fatalf("SettingsPath = %q, want %q", got.SettingsPath, filepath.Join(dir, ".gc", "settings.json")) + } + if got.SettingsRel != filepath.Join(".gc", "settings.json") { + t.Fatalf("SettingsRel = %q, want %q", got.SettingsRel, filepath.Join(".gc", "settings.json")) + } +} diff --git a/internal/config/options_test.go b/internal/config/options_test.go index 359caed140..3e9faa8eaa 100644 --- a/internal/config/options_test.go +++ b/internal/config/options_test.go @@ -714,6 +714,13 @@ func TestBuiltinProviders_CodexHasNilArgsAndOptionDefaults(t *testing.T) { t.Errorf("codex OptionDefaults[permission_mode] = %q, want unrestricted", codex.OptionDefaults["permission_mode"]) } + if codex.OptionDefaults["model"] != "gpt-5.5" { + t.Errorf("codex OptionDefaults[model] = %q, want gpt-5.5", + codex.OptionDefaults["model"]) + } + if !schemaHasChoice(codex.OptionsSchema, "model", "gpt-5.5") { + t.Error("codex OptionsSchema missing model choice gpt-5.5") + } } func TestBuiltinProviders_GeminiHasNilArgsAndOptionDefaults(t *testing.T) { @@ -769,3 +776,17 @@ func TestValidateOptionDefaults_InvalidValue(t *testing.T) { t.Errorf("unexpected error: %v", err) } } + +func schemaHasChoice(schema []ProviderOption, key, value string) bool { + for _, opt := range schema { + if opt.Key != key { + continue + } + for _, choice := range opt.Choices { + if choice.Value == value { + return true + } + } + } + return false +} diff --git a/internal/config/pack.go b/internal/config/pack.go index acaa516748..745de5580c 100644 --- a/internal/config/pack.go +++ b/internal/config/pack.go @@ -1559,6 +1559,10 @@ func deepCopyProviderSpec(in ProviderSpec) ProviderSpec { out.OptionDefaults = deepCopyStringMap(in.OptionDefaults) out.OptionsSchema = deepCopyProviderOptions(in.OptionsSchema) out.PrintArgs = append([]string(nil), in.PrintArgs...) + if in.ACPArgs != nil { + out.ACPArgs = make([]string, len(in.ACPArgs)) + copy(out.ACPArgs, in.ACPArgs) + } out.Base = copyStringPtr(in.Base) out.EmitsPermissionWarning = copyBoolPtr(in.EmitsPermissionWarning) out.SupportsACP = copyBoolPtr(in.SupportsACP) diff --git a/internal/config/patch.go b/internal/config/patch.go index 2b3afbdebf..3a84559fbd 100644 --- a/internal/config/patch.go +++ b/internal/config/patch.go @@ -180,8 +180,12 @@ type ProviderPatch struct { Base **string `toml:"base,omitempty"` // Command overrides the provider command. Command *string `toml:"command,omitempty"` + // ACPCommand overrides the provider command for ACP transport sessions. + ACPCommand *string `toml:"acp_command,omitempty"` // Args overrides the provider args. Args []string `toml:"args,omitempty"` + // ACPArgs overrides the provider args for ACP transport sessions. + ACPArgs []string `toml:"acp_args,omitempty"` // ArgsAppend overrides the provider args_append list. ArgsAppend []string `toml:"args_append,omitempty"` // OptionsSchemaMerge overrides the options_schema merge mode. @@ -451,10 +455,17 @@ func applyProviderPatch(cfg *City, patch *ProviderPatch) error { if patch.Command != nil { newSpec.Command = *patch.Command } + if patch.ACPCommand != nil { + newSpec.ACPCommand = *patch.ACPCommand + } if len(patch.Args) > 0 { newSpec.Args = make([]string, len(patch.Args)) copy(newSpec.Args, patch.Args) } + if patch.ACPArgs != nil { + newSpec.ACPArgs = make([]string, len(patch.ACPArgs)) + copy(newSpec.ACPArgs, patch.ACPArgs) + } if len(patch.ArgsAppend) > 0 { newSpec.ArgsAppend = make([]string, len(patch.ArgsAppend)) copy(newSpec.ArgsAppend, patch.ArgsAppend) @@ -487,10 +498,17 @@ func applyProviderPatch(cfg *City, patch *ProviderPatch) error { if patch.Command != nil { spec.Command = *patch.Command } + if patch.ACPCommand != nil { + spec.ACPCommand = *patch.ACPCommand + } if len(patch.Args) > 0 { spec.Args = make([]string, len(patch.Args)) copy(spec.Args, patch.Args) } + if patch.ACPArgs != nil { + spec.ACPArgs = make([]string, len(patch.ACPArgs)) + copy(spec.ACPArgs, patch.ACPArgs) + } if len(patch.ArgsAppend) > 0 { spec.ArgsAppend = make([]string, len(patch.ArgsAppend)) copy(spec.ArgsAppend, patch.ArgsAppend) diff --git a/internal/config/patch_test.go b/internal/config/patch_test.go index 7e42789d6a..e6277effb4 100644 --- a/internal/config/patch_test.go +++ b/internal/config/patch_test.go @@ -260,6 +260,8 @@ func TestApplyPatches_ProviderDeepMerge(t *testing.T) { Providers: map[string]ProviderSpec{ "custom": { Command: "agent", + ACPCommand: "agent-acp", + ACPArgs: []string{"serve"}, PromptMode: "arg", Env: map[string]string{"KEY": "val"}, }, @@ -268,10 +270,12 @@ func TestApplyPatches_ProviderDeepMerge(t *testing.T) { err := ApplyPatches(cfg, Patches{ Providers: []ProviderPatch{ { - Name: "custom", - Command: ptrStr("new-agent"), - Env: map[string]string{"KEY2": "val2"}, - EnvRemove: []string{"KEY"}, + Name: "custom", + Command: ptrStr("new-agent"), + ACPCommand: ptrStr("new-agent-acp"), + ACPArgs: []string{"rpc", "--stdio"}, + Env: map[string]string{"KEY2": "val2"}, + EnvRemove: []string{"KEY"}, }, }, }) @@ -282,6 +286,12 @@ func TestApplyPatches_ProviderDeepMerge(t *testing.T) { if p.Command != "new-agent" { t.Errorf("Command = %q, want %q", p.Command, "new-agent") } + if p.ACPCommand != "new-agent-acp" { + t.Errorf("ACPCommand = %q, want %q", p.ACPCommand, "new-agent-acp") + } + if got := strings.Join(p.ACPArgs, " "); got != "rpc --stdio" { + t.Errorf("ACPArgs = %q, want %q", got, "rpc --stdio") + } if p.PromptMode != "arg" { t.Errorf("PromptMode = %q, want %q (unchanged)", p.PromptMode, "arg") } @@ -298,6 +308,8 @@ func TestApplyPatches_ProviderReplace(t *testing.T) { Providers: map[string]ProviderSpec{ "custom": { Command: "old-agent", + ACPCommand: "old-agent-acp", + ACPArgs: []string{"serve"}, PromptMode: "arg", Env: map[string]string{"SECRET": "hidden"}, }, @@ -306,9 +318,11 @@ func TestApplyPatches_ProviderReplace(t *testing.T) { err := ApplyPatches(cfg, Patches{ Providers: []ProviderPatch{ { - Name: "custom", - Replace: true, - Command: ptrStr("new-agent"), + Name: "custom", + Replace: true, + Command: ptrStr("new-agent"), + ACPCommand: ptrStr("new-agent-acp"), + ACPArgs: []string{"rpc"}, }, }, }) @@ -319,6 +333,12 @@ func TestApplyPatches_ProviderReplace(t *testing.T) { if p.Command != "new-agent" { t.Errorf("Command = %q, want %q", p.Command, "new-agent") } + if p.ACPCommand != "new-agent-acp" { + t.Errorf("ACPCommand = %q, want %q", p.ACPCommand, "new-agent-acp") + } + if got := strings.Join(p.ACPArgs, " "); got != "rpc" { + t.Errorf("ACPArgs = %q, want %q", got, "rpc") + } // Replace clears fields not in patch. if p.PromptMode != "" { t.Errorf("PromptMode = %q, want empty (replaced)", p.PromptMode) diff --git a/internal/config/provider.go b/internal/config/provider.go index 7e9e6e327c..e4e8521e7a 100644 --- a/internal/config/provider.go +++ b/internal/config/provider.go @@ -1,6 +1,8 @@ package config import ( + "strings" + "github.com/gastownhall/gascity/internal/shellquote" workerbuiltin "github.com/gastownhall/gascity/internal/worker/builtin" ) @@ -127,6 +129,12 @@ type ProviderSpec struct { // Defaults to the cheapest/fastest model for each provider. // Examples: "haiku" (claude), "o4-mini" (codex), "gemini-2.5-flash" (gemini) TitleModel string `toml:"title_model,omitempty"` + // ACPCommand overrides Command when the session transport is ACP. + // When empty, Command is used for both tmux and ACP transports. + ACPCommand string `toml:"acp_command,omitempty"` + // ACPArgs overrides Args when the session transport is ACP. + // When nil, Args is used for both tmux and ACP transports. + ACPArgs []string `toml:"acp_args,omitempty"` } // Reserved prefixes for the Base field. @@ -187,6 +195,8 @@ type ResolvedProvider struct { OptionsSchema []ProviderOption PrintArgs []string TitleModel string + ACPCommand string + ACPArgs []string // EffectiveDefaults is the fully-merged option default map. // Computed from: schema Default -> provider OptionDefaults -> agent OptionDefaults. // Used by ResolveDefaultArgs() to produce CLI flags and by the API to @@ -202,6 +212,71 @@ func (rp *ResolvedProvider) CommandString() string { return rp.Command + " " + shellquote.Join(rp.Args) } +// ACPCommandString returns the command line for ACP transport sessions. +// Each field falls back independently: ACPCommand defaults to Command, +// and ACPArgs defaults to Args, so partial overrides are supported. +func (rp *ResolvedProvider) ACPCommandString() string { + cmd := rp.ACPCommand + args := rp.ACPArgs + if cmd == "" { + cmd = rp.Command + } + if args == nil { + args = rp.Args + } + if len(args) == 0 { + return cmd + } + return cmd + " " + shellquote.Join(args) +} + +// DefaultSessionTransport returns the transport used for provider-backed +// sessions when no template-level session override exists. +func (rp *ResolvedProvider) DefaultSessionTransport() string { + if rp == nil || !rp.SupportsACP { + return "" + } + family := strings.TrimSpace(rp.BuiltinAncestor) + if family == "" { + family = strings.TrimSpace(rp.Kind) + } + if family == "" { + family = strings.TrimSpace(rp.Name) + } + if family == "opencode" { + return "acp" + } + return "" +} + +// ProviderSessionCreateTransport returns the transport to use when creating a +// provider-backed session without any template-level session override. +func (rp *ResolvedProvider) ProviderSessionCreateTransport() string { + if rp == nil || !rp.SupportsACP { + return "" + } + if transport := rp.DefaultSessionTransport(); transport != "" { + return transport + } + if strings.TrimSpace(rp.ACPCommand) != "" || rp.ACPArgs != nil { + return "acp" + } + return "" +} + +// ResolveSessionCreateTransport returns the transport to use when creating a +// fresh session from an agent/template configuration. +func ResolveSessionCreateTransport(agentSession string, resolved *ResolvedProvider) string { + agentSession = strings.TrimSpace(agentSession) + if agentSession != "" { + return agentSession + } + if resolved == nil { + return "" + } + return strings.TrimSpace(resolved.ProviderSessionCreateTransport()) +} + // TitleModelFlagArgs resolves the TitleModel key against the "model" // OptionsSchema entry. Returns the CLI flag args for the title model, // or nil if TitleModel is empty or not found in the schema. @@ -307,6 +382,8 @@ func providerSpecFromWorker(spec workerbuiltin.BuiltinProviderSpec) ProviderSpec OptionsSchema: providerOptionsFromWorker(spec.OptionsSchema), PrintArgs: cloneStrings(spec.PrintArgs), TitleModel: spec.TitleModel, + ACPCommand: spec.ACPCommand, + ACPArgs: cloneStrings(spec.ACPArgs), } } diff --git a/internal/config/provider_test.go b/internal/config/provider_test.go index 99e47ef8cb..8fb9b48fd3 100644 --- a/internal/config/provider_test.go +++ b/internal/config/provider_test.go @@ -1,6 +1,7 @@ package config import ( + "reflect" "testing" ) @@ -139,6 +140,34 @@ func TestBuiltinProvidersGemini(t *testing.T) { } } +func TestBuiltinProvidersCursor(t *testing.T) { + p := BuiltinProviders()["cursor"] + if p.Command != "cursor-agent" { + t.Errorf("Command = %q, want %q", p.Command, "cursor-agent") + } + if len(p.Args) != 1 || p.Args[0] != "-f" { + t.Errorf("Args = %v, want [-f]", p.Args) + } + if p.PromptMode != "arg" { + t.Errorf("PromptMode = %q, want %q", p.PromptMode, "arg") + } + if p.ReadyPromptPrefix != "\u2192 " { + t.Errorf("ReadyPromptPrefix = %q, want %q", p.ReadyPromptPrefix, "\u2192 ") + } + if p.ReadyDelayMs != 10000 { + t.Errorf("ReadyDelayMs = %d, want 10000", p.ReadyDelayMs) + } + if len(p.ProcessNames) != 1 || p.ProcessNames[0] != "cursor-agent" { + t.Errorf("ProcessNames = %v, want [cursor-agent]", p.ProcessNames) + } + if !derefBool(p.SupportsHooks) { + t.Error("SupportsHooks = false, want true") + } + if p.InstructionsFile != "AGENTS.md" { + t.Errorf("InstructionsFile = %q, want %q", p.InstructionsFile, "AGENTS.md") + } +} + func TestBuiltinProvidersReturnsNewMap(t *testing.T) { a := BuiltinProviders() b := BuiltinProviders() @@ -157,6 +186,12 @@ func TestBuiltinProvidersOpenCode(t *testing.T) { if p.Command != "opencode" { t.Errorf("Command = %q, want %q", p.Command, "opencode") } + if p.ACPCommand != "" { + t.Errorf("ACPCommand = %q, want empty fallback to Command", p.ACPCommand) + } + if !reflect.DeepEqual(p.ACPArgs, []string{"acp"}) { + t.Errorf("ACPArgs = %v, want [acp]", p.ACPArgs) + } if p.PromptMode != "none" { t.Errorf("PromptMode = %q, want %q", p.PromptMode, "none") } @@ -239,3 +274,196 @@ func TestCommandStringQuotesShellMetacharacters(t *testing.T) { t.Errorf("CommandString() = %q, want %q", got, want) } } + +func TestACPCommandString(t *testing.T) { + tests := []struct { + name string + rp ResolvedProvider + want string + }{ + { + name: "FullOverride", + rp: ResolvedProvider{ + Command: "opencode", + Args: []string{"--verbose"}, + ACPCommand: "opencode-acp", + ACPArgs: []string{"--json-rpc"}, + }, + want: "opencode-acp --json-rpc", + }, + { + name: "FallbackToCommand", + rp: ResolvedProvider{ + Command: "opencode", + Args: []string{"--verbose"}, + }, + want: "opencode --verbose", + }, + { + name: "PartialOverride_CommandOnly", + rp: ResolvedProvider{ + Command: "opencode", + Args: []string{"--verbose"}, + ACPCommand: "opencode-acp", + }, + want: "opencode-acp --verbose", + }, + { + name: "PartialOverride_ArgsOnly", + rp: ResolvedProvider{ + Command: "opencode", + Args: []string{"--verbose"}, + ACPArgs: []string{"--json-rpc"}, + }, + want: "opencode --json-rpc", + }, + { + name: "EmptyACPArgs", + rp: ResolvedProvider{ + Command: "opencode", + Args: []string{"--verbose"}, + ACPCommand: "opencode-acp", + ACPArgs: []string{}, + }, + want: "opencode-acp", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.rp.ACPCommandString() + if got != tt.want { + t.Errorf("ACPCommandString() = %q, want %q", got, tt.want) + } + }) + } + + // Verify FallbackToCommand produces same result as CommandString(). + t.Run("FallbackMatchesCommandString", func(t *testing.T) { + rp := &ResolvedProvider{Command: "opencode", Args: []string{"--verbose"}} + if rp.ACPCommandString() != rp.CommandString() { + t.Errorf("ACPCommandString() = %q, but CommandString() = %q — should match when no ACP overrides", + rp.ACPCommandString(), rp.CommandString()) + } + }) +} + +func TestDefaultSessionTransportOpenCodeFamilyDefaultsToACP(t *testing.T) { + tests := []struct { + name string + rp ResolvedProvider + }{ + { + name: "direct builtin name", + rp: ResolvedProvider{ + Name: "opencode", + SupportsACP: true, + }, + }, + { + name: "builtin ancestor", + rp: ResolvedProvider{ + Name: "custom-opencode", + BuiltinAncestor: "opencode", + SupportsACP: true, + }, + }, + { + name: "deprecated kind fallback", + rp: ResolvedProvider{ + Name: "custom-opencode", + Kind: "opencode", + SupportsACP: true, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.rp.DefaultSessionTransport(); got != "acp" { + t.Fatalf("DefaultSessionTransport() = %q, want %q", got, "acp") + } + }) + } +} + +func TestDefaultSessionTransportSupportsACPDoesNotImplyACPDefault(t *testing.T) { + rp := &ResolvedProvider{ + Name: "custom-acp", + SupportsACP: true, + } + if got := rp.DefaultSessionTransport(); got != "" { + t.Fatalf("DefaultSessionTransport() = %q, want empty default transport", got) + } +} + +func TestProviderSessionCreateTransportUsesExplicitACPOverrides(t *testing.T) { + tests := []struct { + name string + rp ResolvedProvider + }{ + { + name: "explicit acp command", + rp: ResolvedProvider{ + Name: "custom-acp", + SupportsACP: true, + ACPCommand: "/bin/custom-acp", + }, + }, + { + name: "explicit acp args", + rp: ResolvedProvider{ + Name: "custom-acp", + SupportsACP: true, + ACPArgs: []string{"acp"}, + }, + }, + { + name: "opencode family remains acp", + rp: ResolvedProvider{ + Name: "custom-opencode", + BuiltinAncestor: "opencode", + SupportsACP: true, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.rp.ProviderSessionCreateTransport(); got != "acp" { + t.Fatalf("ProviderSessionCreateTransport() = %q, want %q", got, "acp") + } + }) + } +} + +func TestProviderSessionCreateTransportSupportsACPAloneStaysDefault(t *testing.T) { + rp := &ResolvedProvider{ + Name: "custom-acp", + SupportsACP: true, + } + if got := rp.ProviderSessionCreateTransport(); got != "" { + t.Fatalf("ProviderSessionCreateTransport() = %q, want empty transport", got) + } +} + +func TestResolveSessionCreateTransportPrefersAgentSessionOverride(t *testing.T) { + got := ResolveSessionCreateTransport("acp", &ResolvedProvider{ + Name: "custom-acp", + SupportsACP: true, + }) + if got != "acp" { + t.Fatalf("ResolveSessionCreateTransport() = %q, want %q", got, "acp") + } +} + +func TestResolveSessionCreateTransportFallsBackToProviderCreateTransport(t *testing.T) { + got := ResolveSessionCreateTransport("", &ResolvedProvider{ + Name: "custom-acp", + SupportsACP: true, + ACPCommand: "/bin/echo", + }) + if got != "acp" { + t.Fatalf("ResolveSessionCreateTransport() = %q, want %q", got, "acp") + } +} diff --git a/internal/config/resolve.go b/internal/config/resolve.go index db34e8a7df..560615d4d8 100644 --- a/internal/config/resolve.go +++ b/internal/config/resolve.go @@ -250,6 +250,9 @@ func MergeProviderOverBuiltin(base, city ProviderSpec) ProviderSpec { if city.TitleModel != "" { result.TitleModel = city.TitleModel } + if city.ACPCommand != "" { + result.ACPCommand = city.ACPCommand + } // Slice fields: replace entirely when non-nil. if city.Args != nil { @@ -274,6 +277,9 @@ func MergeProviderOverBuiltin(base, city ProviderSpec) ProviderSpec { if city.PrintArgs != nil { result.PrintArgs = city.PrintArgs } + if city.ACPArgs != nil { + result.ACPArgs = city.ACPArgs + } // Map fields: merge additively (city keys win). if city.PermissionModes != nil { @@ -486,6 +492,7 @@ func specToResolved(name string, spec *ProviderSpec) *ResolvedProvider { ResumeCommand: spec.ResumeCommand, SessionIDFlag: spec.SessionIDFlag, TitleModel: spec.TitleModel, + ACPCommand: spec.ACPCommand, } // Deep-copy OptionsSchema to avoid aliasing the spec's slice. if len(spec.OptionsSchema) > 0 { @@ -553,6 +560,10 @@ func specToResolved(name string, spec *ProviderSpec) *ResolvedProvider { rp.PrintArgs = make([]string, len(spec.PrintArgs)) copy(rp.PrintArgs, spec.PrintArgs) } + if spec.ACPArgs != nil { + rp.ACPArgs = make([]string, len(spec.ACPArgs)) + copy(rp.ACPArgs, spec.ACPArgs) + } return rp } @@ -700,6 +711,13 @@ func resolvedChainToSpec(r ResolvedProvider, leaf ProviderSpec) ProviderSpec { if r.TitleModel != "" { out.TitleModel = r.TitleModel } + if r.ACPCommand != "" { + out.ACPCommand = r.ACPCommand + } + if r.ACPArgs != nil { + out.ACPArgs = make([]string, len(r.ACPArgs)) + copy(out.ACPArgs, r.ACPArgs) + } if r.PrintArgs != nil { out.PrintArgs = append([]string(nil), r.PrintArgs...) } diff --git a/internal/config/resolve_test.go b/internal/config/resolve_test.go index 7d6f97b624..175270a04f 100644 --- a/internal/config/resolve_test.go +++ b/internal/config/resolve_test.go @@ -112,7 +112,11 @@ func TestResolveProviderWorkspaceProvider(t *testing.T) { t.Errorf("CommandString() = %q, want %q", rp.CommandString(), "codex") } defaultArgs := rp.ResolveDefaultArgs() - codexWantArgs := []string{"--dangerously-bypass-approvals-and-sandbox", "-c", "model_reasoning_effort=xhigh"} + codexWantArgs := []string{ + "--dangerously-bypass-approvals-and-sandbox", + "--model", "gpt-5.5", + "-c", "model_reasoning_effort=xhigh", + } if len(defaultArgs) != len(codexWantArgs) { t.Errorf("ResolveDefaultArgs() = %v, want %v", defaultArgs, codexWantArgs) } else { @@ -856,6 +860,31 @@ func TestMergeProviderOverBuiltin(t *testing.T) { } } +func TestResolveProviderBuiltinOpenCodeCustomCommandKeepsACPArgsOnCustomBinary(t *testing.T) { + base := "builtin:opencode" + cityProviders := map[string]ProviderSpec{ + "custom-opencode": { + Base: &base, + Command: "custom-opencode", + }, + } + agent := &Agent{Name: "worker", Provider: "custom-opencode"} + + rp, err := ResolveProvider(agent, nil, cityProviders, lookPathOnly("custom-opencode")) + if err != nil { + t.Fatalf("ResolveProvider: %v", err) + } + if rp.Command != "custom-opencode" { + t.Fatalf("Command = %q, want custom-opencode", rp.Command) + } + if rp.ACPCommand != "" { + t.Fatalf("ACPCommand = %q, want empty fallback to Command", rp.ACPCommand) + } + if got := rp.ACPCommandString(); got != "custom-opencode acp" { + t.Fatalf("ACPCommandString() = %q, want custom-opencode acp", got) + } +} + // --- Tri-state capability bool tests --- // // These verify the three-way *bool semantics for SupportsHooks, @@ -1235,6 +1264,8 @@ func TestMergeProviderOverBuiltinFieldSync(t *testing.T) { OptionsSchema: []ProviderOption{{Key: "model"}}, PrintArgs: []string{"-p"}, TitleModel: "haiku", + ACPCommand: "custom-acp", + ACPArgs: []string{"acp-mode"}, } // Verify every field on city is non-zero (catches new fields not added to test data). diff --git a/internal/configedit/configedit.go b/internal/configedit/configedit.go index 33bf295af1..57f9bad728 100644 --- a/internal/configedit/configedit.go +++ b/internal/configedit/configedit.go @@ -711,7 +711,9 @@ type ProviderUpdate struct { DisplayName *string Base **string Command *string + ACPCommand *string Args []string // nil = not set, non-nil = replace + ACPArgs []string // nil = not set, non-nil = replace ArgsAppend []string // nil = not set, non-nil = replace PromptMode *string PromptFlag *string @@ -760,10 +762,17 @@ func (e *Editor) UpdateProvider(name string, patch ProviderUpdate) error { if patch.Command != nil { spec.Command = *patch.Command } + if patch.ACPCommand != nil { + spec.ACPCommand = *patch.ACPCommand + } if patch.Args != nil { spec.Args = make([]string, len(patch.Args)) copy(spec.Args, patch.Args) } + if patch.ACPArgs != nil { + spec.ACPArgs = make([]string, len(patch.ACPArgs)) + copy(spec.ACPArgs, patch.ACPArgs) + } if patch.ArgsAppend != nil { spec.ArgsAppend = make([]string, len(patch.ArgsAppend)) copy(spec.ArgsAppend, patch.ArgsAppend) diff --git a/internal/configedit/configedit_test.go b/internal/configedit/configedit_test.go index afcec12acb..cd91011859 100644 --- a/internal/configedit/configedit_test.go +++ b/internal/configedit/configedit_test.go @@ -1198,9 +1198,12 @@ func TestUpdateProvider(t *testing.T) { ed := configedit.NewEditor(fsys.OSFS{}, path) newCmd := "updated-cli" + newACPCmd := "updated-cli-acp" newName := "Updated Agent" err := ed.UpdateProvider("custom", configedit.ProviderUpdate{ Command: &newCmd, + ACPCommand: &newACPCmd, + ACPArgs: []string{"rpc", "--stdio"}, DisplayName: &newName, }) if err != nil { @@ -1212,6 +1215,12 @@ func TestUpdateProvider(t *testing.T) { if got.Command != "updated-cli" { t.Errorf("command = %q, want %q", got.Command, "updated-cli") } + if got.ACPCommand != "updated-cli-acp" { + t.Errorf("acp_command = %q, want %q", got.ACPCommand, "updated-cli-acp") + } + if len(got.ACPArgs) != 2 || got.ACPArgs[0] != "rpc" || got.ACPArgs[1] != "--stdio" { + t.Errorf("acp_args = %#v, want [rpc --stdio]", got.ACPArgs) + } if got.DisplayName != "Updated Agent" { t.Errorf("display_name = %q, want %q", got.DisplayName, "Updated Agent") } diff --git a/internal/events/events.go b/internal/events/events.go index a3e00e7d3b..92847e851c 100644 --- a/internal/events/events.go +++ b/internal/events/events.go @@ -17,36 +17,42 @@ import ( // Event type constants. Only types we actually emit today. const ( - SessionWoke = "session.woke" - SessionStopped = "session.stopped" - SessionCrashed = "session.crashed" - BeadCreated = "bead.created" - BeadClosed = "bead.closed" - BeadUpdated = "bead.updated" - MailSent = "mail.sent" - MailRead = "mail.read" - MailArchived = "mail.archived" - MailMarkedRead = "mail.marked_read" - MailMarkedUnread = "mail.marked_unread" - MailReplied = "mail.replied" - MailDeleted = "mail.deleted" - SessionDraining = "session.draining" - SessionUndrained = "session.undrained" - SessionQuarantined = "session.quarantined" - SessionIdleKilled = "session.idle_killed" - SessionSuspended = "session.suspended" - SessionUpdated = "session.updated" - ConvoyCreated = "convoy.created" - ConvoyClosed = "convoy.closed" - ControllerStarted = "controller.started" - ControllerStopped = "controller.stopped" - CitySuspended = "city.suspended" - CityResumed = "city.resumed" - OrderFired = "order.fired" - OrderCompleted = "order.completed" - OrderFailed = "order.failed" - ProviderSwapped = "provider.swapped" - WorkerOperation = "worker.operation" + SessionWoke = "session.woke" + SessionStopped = "session.stopped" + SessionCrashed = "session.crashed" + BeadCreated = "bead.created" + BeadClosed = "bead.closed" + BeadUpdated = "bead.updated" + MailSent = "mail.sent" + MailRead = "mail.read" + MailArchived = "mail.archived" + MailMarkedRead = "mail.marked_read" + MailMarkedUnread = "mail.marked_unread" + MailReplied = "mail.replied" + MailDeleted = "mail.deleted" + SessionDraining = "session.draining" + SessionUndrained = "session.undrained" + SessionQuarantined = "session.quarantined" + SessionIdleKilled = "session.idle_killed" + SessionSuspended = "session.suspended" + SessionUpdated = "session.updated" + ConvoyCreated = "convoy.created" + ConvoyClosed = "convoy.closed" + ControllerStarted = "controller.started" + ControllerStopped = "controller.stopped" + CitySuspended = "city.suspended" + CityResumed = "city.resumed" + CityCreated = "city.created" + CityReady = "city.ready" + CityInitFailed = "city.init_failed" + CityUnregisterRequested = "city.unregister_requested" + CityUnregistered = "city.unregistered" + CityUnregisterFailed = "city.unregister_failed" + OrderFired = "order.fired" + OrderCompleted = "order.completed" + OrderFailed = "order.failed" + ProviderSwapped = "provider.swapped" + WorkerOperation = "worker.operation" // External messaging events. ExtMsgBound = "extmsg.bound" @@ -72,6 +78,8 @@ var KnownEventTypes = []string{ ConvoyCreated, ConvoyClosed, ControllerStarted, ControllerStopped, CitySuspended, CityResumed, + CityCreated, CityReady, CityInitFailed, + CityUnregisterRequested, CityUnregistered, CityUnregisterFailed, OrderFired, OrderCompleted, OrderFailed, ProviderSwapped, WorkerOperation, ExtMsgBound, ExtMsgUnbound, ExtMsgGroupCreated, diff --git a/internal/fsys/atomic.go b/internal/fsys/atomic.go index dd87452481..5534a5606c 100644 --- a/internal/fsys/atomic.go +++ b/internal/fsys/atomic.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "os" + "reflect" "strconv" "time" ) @@ -34,14 +35,111 @@ func WriteFileAtomic(fs FS, path string, data []byte, perm os.FileMode) error { } // WriteFileIfChangedAtomic writes data to path atomically only when the -// existing on-disk bytes differ. Returns nil with no write when the -// content already matches. A read error other than "not exist" is -// ignored and the write proceeds — this is a best-effort optimization to -// avoid churning mtime (and fsnotify watchers) on no-op writes, not a -// safety check. +// existing on-disk bytes differ. Returns nil with no write when the content +// already matches on a stable regular file. Read or stat errors are ignored +// and the write proceeds — this is a best-effort optimization to avoid +// churning mtime on no-op writes, not a safety check. func WriteFileIfChangedAtomic(fs FS, path string, data []byte, perm os.FileMode) error { - if existing, err := fs.ReadFile(path); err == nil && bytes.Equal(existing, data) { - return nil + if info, err := fs.Lstat(path); err == nil && info.Mode().IsRegular() { + if snapshot, err := readRegularFileSnapshot(fs, path); err == nil && bytes.Equal(snapshot.data, data) { + if info, err := fs.Lstat(path); err == nil && info.Mode().IsRegular() { + if !snapshot.hasID { + return WriteFileAtomic(fs, path, data, perm) + } + currentID, ok := fileIdentityFromInfo(info) + if !ok || currentID != snapshot.id { + return WriteFileAtomic(fs, path, data, perm) + } + return nil + } + } } return WriteFileAtomic(fs, path, data, perm) } + +// WriteFileIfContentOrModeChangedAtomic writes data to path atomically when +// the existing on-disk bytes, file type, or permissions differ. Returns nil +// with no write when the path is already a regular file with matching content +// and mode. Symlinks and other non-regular entries are replaced without first +// reading through them. Read or stat errors are ignored and the write proceeds. +func WriteFileIfContentOrModeChangedAtomic(fs FS, path string, data []byte, perm os.FileMode) error { + if info, err := fs.Lstat(path); err == nil && info.Mode().IsRegular() && comparableMode(info.Mode()) == comparableMode(perm) { + if snapshot, err := readRegularFileSnapshot(fs, path); err == nil && bytes.Equal(snapshot.data, data) { + if info, err := fs.Lstat(path); err == nil && info.Mode().IsRegular() && comparableMode(info.Mode()) == comparableMode(perm) { + if !snapshot.hasID { + return WriteFileAtomic(fs, path, data, perm) + } + currentID, ok := fileIdentityFromInfo(info) + if !ok || currentID != snapshot.id { + return WriteFileAtomic(fs, path, data, perm) + } + return nil + } + } + } + return WriteFileAtomic(fs, path, data, perm) +} + +type regularFileSnapshotReader interface { + readRegularFileSnapshot(name string) (regularFileSnapshot, error) +} + +type regularFileSnapshot struct { + data []byte + id fileIdentity + hasID bool +} + +type fileIdentity struct { + dev uint64 + ino uint64 +} + +func readRegularFileSnapshot(fs FS, path string) (regularFileSnapshot, error) { + if reader, ok := fs.(regularFileSnapshotReader); ok { + return reader.readRegularFileSnapshot(path) + } + return regularFileSnapshot{}, &os.PathError{Op: "open", Path: path, Err: os.ErrInvalid} +} + +func comparableMode(mode os.FileMode) os.FileMode { + return mode & (os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky) +} + +func fileIdentityFromInfo(info os.FileInfo) (fileIdentity, bool) { + return fileIdentityFromSys(info.Sys()) +} + +func fileIdentityFromSys(sys any) (fileIdentity, bool) { + // Signed stat fields follow Go's direct int-to-uint conversion so the + // Fstat and Lstat paths agree on device identity across Unix variants. + stat := reflect.Indirect(reflect.ValueOf(sys)) + if !stat.IsValid() { + return fileIdentity{}, false + } + dev := stat.FieldByName("Dev") + ino := stat.FieldByName("Ino") + if !dev.IsValid() || !ino.IsValid() { + return fileIdentity{}, false + } + devValue, ok := numericFieldToUint64(dev) + if !ok { + return fileIdentity{}, false + } + inoValue, ok := numericFieldToUint64(ino) + if !ok { + return fileIdentity{}, false + } + return fileIdentity{dev: devValue, ino: inoValue}, true +} + +func numericFieldToUint64(v reflect.Value) (uint64, bool) { + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return uint64(v.Int()), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint(), true + default: + return 0, false + } +} diff --git a/internal/fsys/atomic_internal_test.go b/internal/fsys/atomic_internal_test.go new file mode 100644 index 0000000000..08cc8e9149 --- /dev/null +++ b/internal/fsys/atomic_internal_test.go @@ -0,0 +1,212 @@ +package fsys + +import ( + "os" + "testing" + "time" +) + +func TestWriteFileIfContentOrModeChangedAtomic_RewritesWhenIdentityChanges(t *testing.T) { + fs := &identityChangingFS{data: []byte("#!/bin/sh\n")} + + if err := WriteFileIfContentOrModeChangedAtomic(fs, "/script.sh", fs.data, 0o755); err != nil { + t.Fatalf("WriteFileIfContentOrModeChangedAtomic: %v", err) + } + + if !fs.renamed { + t.Fatalf("identity-changing file was not rewritten") + } +} + +func TestWriteFileIfChangedAtomic_RewritesWhenIdentityChanges(t *testing.T) { + fs := &identityChangingFS{data: []byte("hello = true\n")} + + if err := WriteFileIfChangedAtomic(fs, "/config.toml", fs.data, 0o644); err != nil { + t.Fatalf("WriteFileIfChangedAtomic: %v", err) + } + + if !fs.renamed { + t.Fatalf("identity-changing file was not rewritten") + } +} + +func TestWriteFileIfContentOrModeChangedAtomic_RewritesWithoutSnapshotIdentity(t *testing.T) { + fs := &noIdentitySnapshotFS{data: []byte("#!/bin/sh\n")} + + if err := WriteFileIfContentOrModeChangedAtomic(fs, "/script.sh", fs.data, 0o755); err != nil { + t.Fatalf("WriteFileIfContentOrModeChangedAtomic: %v", err) + } + + if !fs.renamed { + t.Fatalf("no-identity snapshot was not rewritten") + } +} + +func TestWriteFileIfChangedAtomic_RewritesWithoutSnapshotIdentity(t *testing.T) { + fs := &noIdentitySnapshotFS{data: []byte("hello = true\n")} + + if err := WriteFileIfChangedAtomic(fs, "/config.toml", fs.data, 0o644); err != nil { + t.Fatalf("WriteFileIfChangedAtomic: %v", err) + } + + if !fs.renamed { + t.Fatalf("no-identity snapshot was not rewritten") + } +} + +func TestFileIdentityFromSys_NormalizesSignedDeviceField(t *testing.T) { + id, ok := fileIdentityFromSys(struct { + Dev int32 + Ino uint64 + }{ + Dev: 7, + Ino: 11, + }) + if !ok { + t.Fatalf("fileIdentityFromSys returned ok=false for signed Dev field") + } + + want := fileIdentity{dev: 7, ino: 11} + if id != want { + t.Fatalf("fileIdentityFromSys = %#v, want %#v", id, want) + } +} + +func TestFileIdentityFromSys_NormalizesSignedDeviceFieldPointer(t *testing.T) { + id, ok := fileIdentityFromSys(&struct { + Dev int32 + Ino uint64 + }{ + Dev: 7, + Ino: 11, + }) + if !ok { + t.Fatalf("fileIdentityFromSys returned ok=false for pointer-shaped signed Dev field") + } + + want := fileIdentity{dev: 7, ino: 11} + if id != want { + t.Fatalf("fileIdentityFromSys = %#v, want %#v", id, want) + } +} + +func TestFileIdentityFromSys_PreservesNegativeSignedDeviceFieldBits(t *testing.T) { + id, ok := fileIdentityFromSys(struct { + Dev int32 + Ino uint64 + }{ + Dev: -1, + Ino: 11, + }) + if !ok { + t.Fatalf("fileIdentityFromSys returned ok=false for negative signed Dev field") + } + + dev := int32(-1) + want := fileIdentity{dev: uint64(dev), ino: 11} + if id != want { + t.Fatalf("fileIdentityFromSys = %#v, want %#v", id, want) + } +} + +type identityChangingFS struct { + data []byte + snapshotErr error + renamed bool + lstats int +} + +func (f *identityChangingFS) MkdirAll(string, os.FileMode) error { return nil } + +func (f *identityChangingFS) WriteFile(string, []byte, os.FileMode) error { return nil } + +func (f *identityChangingFS) ReadFile(string) ([]byte, error) { return f.data, nil } + +func (f *identityChangingFS) Stat(string) (os.FileInfo, error) { + return identityFileInfo{mode: 0o755, id: fileIdentity{dev: 1, ino: 1}}, nil +} + +func (f *identityChangingFS) Lstat(string) (os.FileInfo, error) { + f.lstats++ + id := fileIdentity{dev: 1, ino: 1} + if f.lstats > 1 { + id = fileIdentity{dev: 1, ino: 2} + } + return identityFileInfo{mode: 0o755, id: id}, nil +} + +func (f *identityChangingFS) ReadDir(string) ([]os.DirEntry, error) { return nil, nil } + +func (f *identityChangingFS) Rename(string, string) error { + f.renamed = true + return nil +} + +func (f *identityChangingFS) Remove(string) error { return nil } + +func (f *identityChangingFS) Chmod(string, os.FileMode) error { return nil } + +func (f *identityChangingFS) readRegularFileSnapshot(string) (regularFileSnapshot, error) { + if f.snapshotErr != nil { + return regularFileSnapshot{}, f.snapshotErr + } + return regularFileSnapshot{ + data: f.data, + id: fileIdentity{dev: 1, ino: 1}, + hasID: true, + }, nil +} + +type identityFileInfo struct { + mode os.FileMode + id fileIdentity +} + +func (i identityFileInfo) Name() string { return "script.sh" } +func (i identityFileInfo) Size() int64 { return int64(len("#!/bin/sh\n")) } +func (i identityFileInfo) Mode() os.FileMode { return i.mode } +func (i identityFileInfo) ModTime() time.Time { return time.Time{} } +func (i identityFileInfo) IsDir() bool { return false } +func (i identityFileInfo) Sys() any { return struct{ Dev, Ino uint64 }{i.id.dev, i.id.ino} } + +var _ FS = (*identityChangingFS)(nil) + +type noIdentitySnapshotFS struct { + data []byte + snapshotErr error + renamed bool +} + +func (f *noIdentitySnapshotFS) MkdirAll(string, os.FileMode) error { return nil } + +func (f *noIdentitySnapshotFS) WriteFile(string, []byte, os.FileMode) error { return nil } + +func (f *noIdentitySnapshotFS) ReadFile(string) ([]byte, error) { return f.data, nil } + +func (f *noIdentitySnapshotFS) Stat(string) (os.FileInfo, error) { + return identityFileInfo{mode: 0o755, id: fileIdentity{dev: 1, ino: 1}}, nil +} + +func (f *noIdentitySnapshotFS) Lstat(string) (os.FileInfo, error) { + return identityFileInfo{mode: 0o755, id: fileIdentity{dev: 1, ino: 1}}, nil +} + +func (f *noIdentitySnapshotFS) ReadDir(string) ([]os.DirEntry, error) { return nil, nil } + +func (f *noIdentitySnapshotFS) Rename(string, string) error { + f.renamed = true + return nil +} + +func (f *noIdentitySnapshotFS) Remove(string) error { return nil } + +func (f *noIdentitySnapshotFS) Chmod(string, os.FileMode) error { return nil } + +func (f *noIdentitySnapshotFS) readRegularFileSnapshot(string) (regularFileSnapshot, error) { + if f.snapshotErr != nil { + return regularFileSnapshot{}, f.snapshotErr + } + return regularFileSnapshot{data: f.data}, nil +} + +var _ FS = (*noIdentitySnapshotFS)(nil) diff --git a/internal/fsys/atomic_test.go b/internal/fsys/atomic_test.go index d7554dc0e6..ed2b5d7583 100644 --- a/internal/fsys/atomic_test.go +++ b/internal/fsys/atomic_test.go @@ -4,6 +4,7 @@ import ( "os" "path/filepath" "testing" + "time" "github.com/gastownhall/gascity/internal/fsys" ) @@ -57,3 +58,210 @@ func TestWriteFileAtomic_Overwrite(t *testing.T) { } } } + +func TestWriteFileIfChangedAtomic_SkipsMatchingContent(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "test.toml") + data := []byte("hello = true\n") + + if err := fsys.WriteFileAtomic(fsys.OSFS{}, path, data, 0o644); err != nil { + t.Fatalf("WriteFileAtomic: %v", err) + } + past := time.Unix(123456789, 0) + if err := os.Chtimes(path, past, past); err != nil { + t.Fatalf("Chtimes: %v", err) + } + + if err := fsys.WriteFileIfChangedAtomic(fsys.OSFS{}, path, data, 0o644); err != nil { + t.Fatalf("WriteFileIfChangedAtomic: %v", err) + } + + info, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + if !info.ModTime().Equal(past) { + t.Fatalf("file was rewritten: modtime = %s, want %s", info.ModTime(), past) + } +} + +func TestWriteFileIfChangedAtomic_SkipsMatchingContentWhenModeDiffers(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "test.toml") + data := []byte("hello = true\n") + + if err := fsys.WriteFileAtomic(fsys.OSFS{}, path, data, 0o644); err != nil { + t.Fatalf("WriteFileAtomic: %v", err) + } + past := time.Unix(123456789, 0) + if err := os.Chtimes(path, past, past); err != nil { + t.Fatalf("Chtimes: %v", err) + } + + if err := fsys.WriteFileIfChangedAtomic(fsys.OSFS{}, path, data, 0o755); err != nil { + t.Fatalf("WriteFileIfChangedAtomic: %v", err) + } + + info, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + if !info.ModTime().Equal(past) { + t.Fatalf("file was rewritten: modtime = %s, want %s", info.ModTime(), past) + } + if info.Mode().Perm() != 0o644 { + t.Fatalf("mode = %v, want unchanged 0644", info.Mode().Perm()) + } +} + +func TestWriteFileIfChangedAtomic_ReplacesMatchingSymlink(t *testing.T) { + dir := t.TempDir() + target := filepath.Join(dir, "target.toml") + link := filepath.Join(dir, "link.toml") + data := []byte("hello = true\n") + + if err := os.WriteFile(target, data, 0o644); err != nil { + t.Fatal(err) + } + if err := os.Symlink(target, link); err != nil { + t.Fatal(err) + } + + if err := fsys.WriteFileIfChangedAtomic(fsys.OSFS{}, link, data, 0o644); err != nil { + t.Fatalf("WriteFileIfChangedAtomic: %v", err) + } + + info, err := os.Lstat(link) + if err != nil { + t.Fatal(err) + } + if info.Mode()&os.ModeSymlink != 0 { + t.Fatalf("matching symlink was preserved, want replacement with regular file") + } +} + +func TestWriteFileIfContentOrModeChangedAtomic_RepairsModeMismatch(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "script.sh") + data := []byte("#!/bin/sh\n") + + if err := os.WriteFile(path, data, 0o644); err != nil { + t.Fatal(err) + } + if err := os.Chmod(path, 0o644); err != nil { + t.Fatal(err) + } + + if err := fsys.WriteFileIfContentOrModeChangedAtomic(fsys.OSFS{}, path, data, 0o755); err != nil { + t.Fatalf("WriteFileIfContentOrModeChangedAtomic: %v", err) + } + + info, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + if info.Mode().Perm() != 0o755 { + t.Fatalf("mode = %v, want 0755", info.Mode().Perm()) + } +} + +func TestWriteFileIfContentOrModeChangedAtomic_RepairsSpecialModeBits(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "script.sh") + data := []byte("#!/bin/sh\n") + + if err := os.WriteFile(path, data, 0o755); err != nil { + t.Fatal(err) + } + if err := os.Chmod(path, 0o4755); err != nil { + t.Fatal(err) + } + info, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + if info.Mode()&os.ModeSetuid == 0 { + t.Skipf("filesystem did not preserve setuid bit in test mode: %v", info.Mode()) + } + + if err := fsys.WriteFileIfContentOrModeChangedAtomic(fsys.OSFS{}, path, data, 0o755); err != nil { + t.Fatalf("WriteFileIfContentOrModeChangedAtomic: %v", err) + } + + info, err = os.Stat(path) + if err != nil { + t.Fatal(err) + } + if info.Mode()&os.ModeSetuid != 0 { + t.Fatalf("setuid bit was not repaired: mode = %v", info.Mode()) + } + if info.Mode().Perm() != 0o755 { + t.Fatalf("mode = %v, want 0755", info.Mode().Perm()) + } +} + +func TestWriteFileIfContentOrModeChangedAtomic_ReplacesMatchingSymlink(t *testing.T) { + dir := t.TempDir() + target := filepath.Join(dir, "target.sh") + link := filepath.Join(dir, "link.sh") + data := []byte("#!/bin/sh\n") + + if err := os.WriteFile(target, data, 0o755); err != nil { + t.Fatal(err) + } + if err := os.Symlink(target, link); err != nil { + t.Fatal(err) + } + + if err := fsys.WriteFileIfContentOrModeChangedAtomic(fsys.OSFS{}, link, data, 0o755); err != nil { + t.Fatalf("WriteFileIfContentOrModeChangedAtomic: %v", err) + } + + info, err := os.Lstat(link) + if err != nil { + t.Fatal(err) + } + if info.Mode()&os.ModeSymlink != 0 { + t.Fatalf("matching symlink was preserved, want replacement with regular file") + } +} + +func TestWriteFileIfContentOrModeChangedAtomic_LstatsBeforeRead(t *testing.T) { + fake := fsys.NewFake() + fake.Files["/target.sh"] = []byte("#!/bin/sh\n") + fake.Symlinks["/link.sh"] = "/target.sh" + + if err := fsys.WriteFileIfContentOrModeChangedAtomic(fake, "/link.sh", []byte("#!/bin/sh\n"), 0o755); err != nil { + t.Fatalf("WriteFileIfContentOrModeChangedAtomic: %v", err) + } + + for i, call := range fake.Calls { + if call.Method == "Lstat" && call.Path == "/link.sh" { + return + } + if call.Method == "ReadFile" && call.Path == "/link.sh" { + t.Fatalf("ReadFile called before Lstat at call %d: %+v", i, fake.Calls) + } + } + t.Fatalf("Lstat(/link.sh) not called; calls=%+v", fake.Calls) +} + +func TestWriteFileIfContentOrModeChangedAtomic_FakeSkipsMatchingContentAndMode(t *testing.T) { + fake := fsys.NewFake() + data := []byte("hello = true\n") + + if err := fsys.WriteFileIfContentOrModeChangedAtomic(fake, "/test.toml", data, 0o644); err != nil { + t.Fatalf("initial WriteFileIfContentOrModeChangedAtomic: %v", err) + } + fake.Calls = nil + + if err := fsys.WriteFileIfContentOrModeChangedAtomic(fake, "/test.toml", data, 0o644); err != nil { + t.Fatalf("second WriteFileIfContentOrModeChangedAtomic: %v", err) + } + + for _, call := range fake.Calls { + if call.Method == "WriteFile" || call.Method == "Rename" || call.Method == "Chmod" { + t.Fatalf("matching fake file should not be rewritten; calls=%+v", fake.Calls) + } + } +} diff --git a/internal/fsys/fake.go b/internal/fsys/fake.go index 0a5bf4f0b5..fff8280b64 100644 --- a/internal/fsys/fake.go +++ b/internal/fsys/fake.go @@ -1,6 +1,7 @@ package fsys import ( + "hash/fnv" "io/fs" "os" "path/filepath" @@ -14,6 +15,7 @@ import ( type Fake struct { Dirs map[string]bool // pre-populated directories Files map[string][]byte // pre-populated files + Modes map[string]os.FileMode Symlinks map[string]string // pre-populated symlinks (path -> target) Errors map[string]error // path → injected error (checked first) Calls []Call // spy log @@ -21,7 +23,7 @@ type Fake struct { // Call records a single method invocation on [Fake]. type Call struct { - Method string // "MkdirAll", "WriteFile", "ReadFile", "Stat", "ReadDir", "Rename", "Remove", or "Chmod" + Method string // "MkdirAll", "WriteFile", "ReadFile", "ReadRegularFile", "Stat", "ReadDir", "Rename", "Remove", or "Chmod" Path string // path argument } @@ -30,33 +32,50 @@ func NewFake() *Fake { return &Fake{ Dirs: make(map[string]bool), Files: make(map[string][]byte), + Modes: make(map[string]os.FileMode), Symlinks: make(map[string]string), Errors: make(map[string]error), } } // MkdirAll records the call and adds the directory (and parents) to Dirs. -func (f *Fake) MkdirAll(path string, _ os.FileMode) error { +func (f *Fake) MkdirAll(path string, perm os.FileMode) error { f.Calls = append(f.Calls, Call{Method: "MkdirAll", Path: path}) if err, ok := f.Errors[path]; ok { return err } + if f.Dirs == nil { + f.Dirs = make(map[string]bool) + } + if f.Modes == nil { + f.Modes = make(map[string]os.FileMode) + } // Record this directory and all parents. for p := filepath.Clean(path); p != "." && p != "/" && p != string(filepath.Separator); p = filepath.Dir(p) { + if !f.Dirs[p] { + f.Modes[p] = perm.Perm() + } f.Dirs[p] = true } return nil } // WriteFile records the call and stores the data in Files. -func (f *Fake) WriteFile(name string, data []byte, _ os.FileMode) error { +func (f *Fake) WriteFile(name string, data []byte, perm os.FileMode) error { f.Calls = append(f.Calls, Call{Method: "WriteFile", Path: name}) if err, ok := f.Errors[name]; ok { return err } cp := make([]byte, len(data)) copy(cp, data) + if f.Files == nil { + f.Files = make(map[string][]byte) + } + if f.Modes == nil { + f.Modes = make(map[string]os.FileMode) + } f.Files[name] = cp + f.Modes[name] = perm.Perm() return nil } @@ -74,6 +93,37 @@ func (f *Fake) ReadFile(name string) ([]byte, error) { return nil, &os.PathError{Op: "read", Path: name, Err: os.ErrNotExist} } +// ReadRegularFile records the call and returns file contents without following +// symlinks or accepting directories. +func (f *Fake) ReadRegularFile(name string) ([]byte, error) { + f.Calls = append(f.Calls, Call{Method: "ReadRegularFile", Path: name}) + if err, ok := f.Errors[name]; ok { + return nil, err + } + if _, ok := f.Symlinks[name]; ok { + return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrInvalid} + } + if f.Dirs[name] { + return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrInvalid} + } + if data, ok := f.Files[name]; ok { + cp := make([]byte, len(data)) + copy(cp, data) + return cp, nil + } + return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist} +} + +// readRegularFileSnapshot returns regular file contents plus a stable fake +// identity for the path. +func (f *Fake) readRegularFileSnapshot(name string) (regularFileSnapshot, error) { + data, err := f.ReadRegularFile(name) + if err != nil { + return regularFileSnapshot{}, err + } + return regularFileSnapshot{data: data, id: fakeIdentity(name), hasID: true}, nil +} + // Stat records the call and returns info based on Dirs/Files maps. // Symlinks are followed — use Lstat to detect them without following. func (f *Fake) Stat(name string) (os.FileInfo, error) { @@ -83,18 +133,18 @@ func (f *Fake) Stat(name string) (os.FileInfo, error) { } if target, ok := f.Symlinks[name]; ok { if f.Dirs[target] { - return fakeFileInfo{name: filepath.Base(name), dir: true}, nil + return fakeFileInfo{name: filepath.Base(name), dir: true, mode: f.modeFor(target), id: fakeIdentity(target), hasID: true}, nil } if data, ok := f.Files[target]; ok { - return fakeFileInfo{name: filepath.Base(name), size: int64(len(data))}, nil + return fakeFileInfo{name: filepath.Base(name), size: int64(len(data)), mode: f.modeFor(target), id: fakeIdentity(target), hasID: true}, nil } return nil, &os.PathError{Op: "stat", Path: name, Err: os.ErrNotExist} } if f.Dirs[name] { - return fakeFileInfo{name: filepath.Base(name), dir: true}, nil + return fakeFileInfo{name: filepath.Base(name), dir: true, mode: f.modeFor(name), id: fakeIdentity(name), hasID: true}, nil } if data, ok := f.Files[name]; ok { - return fakeFileInfo{name: filepath.Base(name), size: int64(len(data))}, nil + return fakeFileInfo{name: filepath.Base(name), size: int64(len(data)), mode: f.modeFor(name), id: fakeIdentity(name), hasID: true}, nil } return nil, &os.PathError{Op: "stat", Path: name, Err: os.ErrNotExist} } @@ -107,13 +157,13 @@ func (f *Fake) Lstat(name string) (os.FileInfo, error) { return nil, err } if _, ok := f.Symlinks[name]; ok { - return fakeFileInfo{name: filepath.Base(name), symlink: true}, nil + return fakeFileInfo{name: filepath.Base(name), symlink: true, id: fakeIdentity(name), hasID: true}, nil } if f.Dirs[name] { - return fakeFileInfo{name: filepath.Base(name), dir: true}, nil + return fakeFileInfo{name: filepath.Base(name), dir: true, mode: f.modeFor(name), id: fakeIdentity(name), hasID: true}, nil } if data, ok := f.Files[name]; ok { - return fakeFileInfo{name: filepath.Base(name), size: int64(len(data))}, nil + return fakeFileInfo{name: filepath.Base(name), size: int64(len(data)), mode: f.modeFor(name), id: fakeIdentity(name), hasID: true}, nil } return nil, &os.PathError{Op: "lstat", Path: name, Err: os.ErrNotExist} } @@ -135,7 +185,7 @@ func (f *Fake) ReadDir(name string) ([]os.DirEntry, error) { base := filepath.Base(d) if !seen[base] { seen[base] = true - entries = append(entries, fakeDirEntry{name: base, dir: true}) + entries = append(entries, fakeDirEntry{name: base, dir: true, mode: f.modeFor(d), id: fakeIdentity(d), hasID: true}) } } } @@ -145,7 +195,7 @@ func (f *Fake) ReadDir(name string) ([]os.DirEntry, error) { base := filepath.Base(p) if !seen[base] { seen[base] = true - entries = append(entries, fakeDirEntry{name: base, size: int64(len(data))}) + entries = append(entries, fakeDirEntry{name: base, size: int64(len(data)), mode: f.modeFor(p), id: fakeIdentity(p), hasID: true}) } } } @@ -165,6 +215,13 @@ func (f *Fake) Rename(oldpath, newpath string) error { if data, ok := f.Files[oldpath]; ok { f.Files[newpath] = data delete(f.Files, oldpath) + if mode, ok := f.Modes[oldpath]; ok { + f.Modes[newpath] = mode + } else { + delete(f.Modes, newpath) + } + delete(f.Modes, oldpath) + delete(f.Symlinks, newpath) return nil } return &os.PathError{Op: "rename", Path: oldpath, Err: os.ErrNotExist} @@ -178,36 +235,56 @@ func (f *Fake) Remove(name string) error { } if _, ok := f.Files[name]; ok { delete(f.Files, name) + delete(f.Modes, name) + return nil + } + if _, ok := f.Symlinks[name]; ok { + delete(f.Symlinks, name) return nil } if f.Dirs[name] { delete(f.Dirs, name) + delete(f.Modes, name) return nil } return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist} } -// Chmod records the call. Mode is not tracked — the spy log is sufficient -// for tests that care about which paths were chmodded. -func (f *Fake) Chmod(name string, _ os.FileMode) error { +// Chmod records the call and updates the stored mode. +func (f *Fake) Chmod(name string, mode os.FileMode) error { f.Calls = append(f.Calls, Call{Method: "Chmod", Path: name}) if err, ok := f.Errors[name]; ok { return err } + if f.Modes == nil { + f.Modes = make(map[string]os.FileMode) + } if _, ok := f.Files[name]; ok { + f.Modes[name] = mode.Perm() return nil } if f.Dirs[name] { + f.Modes[name] = mode.Perm() return nil } return &os.PathError{Op: "chmod", Path: name, Err: os.ErrNotExist} } +func (f *Fake) modeFor(name string) os.FileMode { + if mode, ok := f.Modes[name]; ok { + return mode + } + return 0o755 +} + // --- fake os.FileInfo --- type fakeFileInfo struct { name string size int64 + mode os.FileMode + id fileIdentity + hasID bool dir bool symlink bool } @@ -218,18 +295,29 @@ func (fi fakeFileInfo) Mode() os.FileMode { if fi.symlink { return 0o777 | os.ModeSymlink } - return 0o755 + if fi.dir { + return fi.mode | os.ModeDir + } + return fi.mode } func (fi fakeFileInfo) ModTime() time.Time { return time.Time{} } func (fi fakeFileInfo) IsDir() bool { return fi.dir } -func (fi fakeFileInfo) Sys() any { return nil } +func (fi fakeFileInfo) Sys() any { + if !fi.hasID { + return nil + } + return struct{ Dev, Ino uint64 }{fi.id.dev, fi.id.ino} +} // --- fake os.DirEntry --- type fakeDirEntry struct { - name string - size int64 - dir bool + name string + size int64 + mode os.FileMode + id fileIdentity + hasID bool + dir bool } func (de fakeDirEntry) Name() string { return de.name } @@ -242,7 +330,13 @@ func (de fakeDirEntry) Type() fs.FileMode { } func (de fakeDirEntry) Info() (fs.FileInfo, error) { - return fakeFileInfo{name: de.name, size: de.size, dir: de.dir}, nil + return fakeFileInfo{name: de.name, size: de.size, mode: de.mode, id: de.id, hasID: de.hasID, dir: de.dir}, nil +} + +func fakeIdentity(name string) fileIdentity { + h := fnv.New64a() + _, _ = h.Write([]byte(name)) + return fileIdentity{dev: 1, ino: h.Sum64()} } var ( diff --git a/internal/fsys/fake_test.go b/internal/fsys/fake_test.go index 6eaf083ac2..eae07f515e 100644 --- a/internal/fsys/fake_test.go +++ b/internal/fsys/fake_test.go @@ -23,6 +23,22 @@ func TestFakeStatDir(t *testing.T) { } } +func TestFakeStatDirModeIncludesDirBit(t *testing.T) { + f := NewFake() + f.Dirs["/city/.gc"] = true + + fi, err := f.Stat("/city/.gc") + if err != nil { + t.Fatalf("Stat existing dir: %v", err) + } + if fi.Mode().IsRegular() { + t.Fatalf("directory mode reports regular file: %v", fi.Mode()) + } + if fi.Mode()&os.ModeDir == 0 { + t.Fatalf("directory mode missing ModeDir bit: %v", fi.Mode()) + } +} + func TestFakeStatFile(t *testing.T) { f := NewFake() f.Files["/city/city.toml"] = []byte("hello") @@ -114,6 +130,17 @@ func TestFakeWriteFile(t *testing.T) { } } +func TestFakeWriteFileInitializesModes(t *testing.T) { + f := &Fake{Files: map[string][]byte{}} + + if err := f.WriteFile("/city/run.sh", []byte("#!/bin/sh\n"), 0o755); err != nil { + t.Fatalf("WriteFile: %v", err) + } + if f.Modes["/city/run.sh"] != 0o755 { + t.Fatalf("mode = %v, want 0755", f.Modes["/city/run.sh"]) + } +} + func TestFakeWriteFileError(t *testing.T) { f := NewFake() injected := fmt.Errorf("read-only fs") @@ -159,6 +186,28 @@ func TestFakeReadDir(t *testing.T) { } } +func TestFakeReadDirInfoReportsTrackedMode(t *testing.T) { + f := NewFake() + if err := f.WriteFile("/city/rigs/run.sh", []byte("#!/bin/sh\n"), 0o755); err != nil { + t.Fatalf("WriteFile: %v", err) + } + + entries, err := f.ReadDir("/city/rigs") + if err != nil { + t.Fatalf("ReadDir: %v", err) + } + if len(entries) != 1 { + t.Fatalf("got %d entries, want 1", len(entries)) + } + info, err := entries[0].Info() + if err != nil { + t.Fatalf("Info: %v", err) + } + if info.Mode().Perm() != 0o755 { + t.Fatalf("ReadDir entry mode = %v, want 0755", info.Mode().Perm()) + } +} + func TestFakeReadDirError(t *testing.T) { f := NewFake() injected := fmt.Errorf("no such directory") @@ -203,6 +252,36 @@ func TestFakeRename(t *testing.T) { } } +func TestFakeRenameClearsStaleDestinationMode(t *testing.T) { + f := NewFake() + f.Files["/city/generated.tmp"] = []byte("new") + f.Files["/city/generated"] = []byte("old") + f.Modes["/city/generated"] = 0o644 + + if err := f.Rename("/city/generated.tmp", "/city/generated"); err != nil { + t.Fatalf("Rename: %v", err) + } + + info, err := f.Stat("/city/generated") + if err != nil { + t.Fatalf("Stat: %v", err) + } + if info.Mode().Perm() != 0o755 { + t.Fatalf("renamed file mode = %v, want default 0755", info.Mode().Perm()) + } +} + +func TestFakeChmodInitializesModes(t *testing.T) { + f := &Fake{Files: map[string][]byte{"/city/run.sh": []byte("#!/bin/sh\n")}} + + if err := f.Chmod("/city/run.sh", 0o755); err != nil { + t.Fatalf("Chmod: %v", err) + } + if f.Modes["/city/run.sh"] != 0o755 { + t.Fatalf("mode = %v, want 0755", f.Modes["/city/run.sh"]) + } +} + func TestFakeRenameError(t *testing.T) { f := NewFake() injected := fmt.Errorf("cross-device link") diff --git a/internal/fsys/read_regular_unix.go b/internal/fsys/read_regular_unix.go new file mode 100644 index 0000000000..4ecb675509 --- /dev/null +++ b/internal/fsys/read_regular_unix.go @@ -0,0 +1,53 @@ +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package fsys + +import ( + "io" + "os" + + "golang.org/x/sys/unix" +) + +// ReadRegularFile reads name without following a final symlink. +func (OSFS) ReadRegularFile(name string) ([]byte, error) { + snapshot, err := (OSFS{}).readRegularFileSnapshot(name) + if err != nil { + return nil, err + } + return snapshot.data, nil +} + +// readRegularFileSnapshot reads name without following a final symlink and +// returns the opened file identity for post-read stability checks. +func (OSFS) readRegularFileSnapshot(name string) (regularFileSnapshot, error) { + fd, err := unix.Open(name, unix.O_RDONLY|unix.O_CLOEXEC|unix.O_NOFOLLOW, 0) + if err != nil { + return regularFileSnapshot{}, &os.PathError{Op: "open", Path: name, Err: err} + } + file := os.NewFile(uintptr(fd), name) + if file == nil { + _ = unix.Close(fd) + return regularFileSnapshot{}, &os.PathError{Op: "open", Path: name, Err: os.ErrInvalid} + } + defer func() { + _ = file.Close() + }() + + var stat unix.Stat_t + if err := unix.Fstat(fd, &stat); err != nil { + return regularFileSnapshot{}, &os.PathError{Op: "stat", Path: name, Err: err} + } + if stat.Mode&unix.S_IFMT != unix.S_IFREG { + return regularFileSnapshot{}, &os.PathError{Op: "open", Path: name, Err: os.ErrInvalid} + } + data, err := io.ReadAll(file) + if err != nil { + return regularFileSnapshot{}, &os.PathError{Op: "read", Path: name, Err: err} + } + return regularFileSnapshot{ + data: data, + id: fileIdentity{dev: uint64(stat.Dev), ino: stat.Ino}, //nolint:unconvert // int32 on darwin, uint64 on linux + hasID: true, + }, nil +} diff --git a/internal/fsys/read_regular_unix_internal_test.go b/internal/fsys/read_regular_unix_internal_test.go new file mode 100644 index 0000000000..e1c181a554 --- /dev/null +++ b/internal/fsys/read_regular_unix_internal_test.go @@ -0,0 +1,38 @@ +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package fsys + +import ( + "os" + "path/filepath" + "testing" +) + +func TestReadRegularFileSnapshot_MatchesFileIdentityFromInfo(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "config.toml") + data := []byte("hello = true\n") + if err := os.WriteFile(path, data, 0o644); err != nil { + t.Fatalf("WriteFile: %v", err) + } + + snapshot, err := (OSFS{}).readRegularFileSnapshot(path) + if err != nil { + t.Fatalf("readRegularFileSnapshot: %v", err) + } + if !snapshot.hasID { + t.Fatalf("snapshot missing identity") + } + + info, err := os.Lstat(path) + if err != nil { + t.Fatalf("Lstat: %v", err) + } + id, ok := fileIdentityFromInfo(info) + if !ok { + t.Fatalf("fileIdentityFromInfo returned ok=false") + } + if snapshot.id != id { + t.Fatalf("snapshot.id = %#v, want %#v", snapshot.id, id) + } +} diff --git a/internal/materialize/mcp.go b/internal/materialize/mcp.go index aba778af02..3a8d25b320 100644 --- a/internal/materialize/mcp.go +++ b/internal/materialize/mcp.go @@ -24,6 +24,8 @@ const ( MCPTransportStdio MCPTransport = "stdio" // MCPTransportHTTP is a streamable HTTP MCP server. MCPTransportHTTP MCPTransport = "http" + // MCPTransportSSE is an SSE-connected MCP server. + MCPTransportSSE MCPTransport = "sse" ) // MCPServer is the canonical neutral MCP model after parsing, diff --git a/internal/materialize/mcp_runtime.go b/internal/materialize/mcp_runtime.go new file mode 100644 index 0000000000..c593ef0040 --- /dev/null +++ b/internal/materialize/mcp_runtime.go @@ -0,0 +1,133 @@ +package materialize + +import ( + "os" + "path/filepath" + + "github.com/gastownhall/gascity/internal/config" + "github.com/gastownhall/gascity/internal/git" + "github.com/gastownhall/gascity/internal/runtime" + workdirutil "github.com/gastownhall/gascity/internal/workdir" +) + +// EffectiveMCPForSession loads, expands, and resolves the effective MCP +// catalog for one concrete session context. +func EffectiveMCPForSession( + cfg *config.City, + cityPath string, + agent *config.Agent, + identity string, + workDir string, +) (MCPCatalog, error) { + cfgForMCP := cfg + if cfg != nil && cfg.PackMCPDir == "" { + cityMCPDir := filepath.Join(cityPath, "mcp") + if info, err := os.Stat(cityMCPDir); err == nil && info.IsDir() { + clone := *cfg + clone.PackMCPDir = cityMCPDir + cfgForMCP = &clone + } + } + return EffectiveMCPForAgent(cfgForMCP, agent, MCPTemplateData(cfgForMCP, cityPath, agent, identity, workDir)) +} + +// MCPTemplateData builds the template expansion surface used by MCP catalogs. +func MCPTemplateData( + cfg *config.City, + cityPath string, + agent *config.Agent, + identity string, + workDir string, +) map[string]string { + if agent == nil { + branch := defaultMCPBranch(workDir) + return map[string]string{ + "CityRoot": cityPath, + "AgentName": identity, + "TemplateName": identity, + "WorkDir": workDir, + "Branch": branch, + "DefaultBranch": branch, + } + } + var rigs []config.Rig + if cfg != nil { + rigs = cfg.Rigs + } + rigName := workdirutil.ConfiguredRigName(cityPath, *agent, rigs) + rigRoot := workdirutil.RigRootForName(rigName, rigs) + templateName := agent.QualifiedName() + if agent.PoolName != "" { + templateName = agent.PoolName + } + if templateName == "" { + templateName = identity + } + data := make(map[string]string, len(agent.Env)+11) + for key, value := range agent.Env { + data[key] = value + } + branch := defaultMCPBranch(workDir) + data["CityRoot"] = cityPath + data["AgentName"] = identity + data["TemplateName"] = templateName + data["RigName"] = rigName + data["RigRoot"] = rigRoot + data["WorkDir"] = workDir + data["IssuePrefix"] = mcpRigPrefix(rigName, rigs) + data["Branch"] = branch + data["DefaultBranch"] = branch + data["WorkQuery"] = agent.EffectiveWorkQuery() + data["SlingQuery"] = agent.EffectiveSlingQuery() + return data +} + +// RuntimeMCPServers converts neutral MCP servers into runtime-owned ACP +// session/new server definitions. +func RuntimeMCPServers(servers []MCPServer) []runtime.MCPServerConfig { + if len(servers) == 0 { + return nil + } + out := make([]runtime.MCPServerConfig, 0, len(servers)) + for _, server := range servers { + entry := runtime.MCPServerConfig{ + Name: server.Name, + Command: server.Command, + Args: append([]string(nil), server.Args...), + Env: cloneStringMap(server.Env), + URL: server.URL, + Headers: cloneStringMap(server.Headers), + } + switch server.Transport { + case MCPTransportHTTP: + entry.Transport = runtime.MCPTransportHTTP + case MCPTransportSSE: + entry.Transport = runtime.MCPTransportSSE + default: + entry.Transport = runtime.MCPTransportStdio + } + out = append(out, entry) + } + return runtime.NormalizeMCPServerConfigs(out) +} + +func mcpRigPrefix(rigName string, rigs []config.Rig) string { + for i := range rigs { + if rigs[i].Name == rigName { + return rigs[i].EffectivePrefix() + } + } + return "" +} + +func defaultMCPBranch(dir string) string { + if dir == "" { + return "main" + } + g := git.New(filepath.Clean(dir)) + branch, _ := g.DefaultBranch() + if branch == "" { + return "main" + } + return branch +} diff --git a/internal/materialize/mcp_test.go b/internal/materialize/mcp_test.go index 248483bcf9..ac9b05ef30 100644 --- a/internal/materialize/mcp_test.go +++ b/internal/materialize/mcp_test.go @@ -9,6 +9,7 @@ import ( "github.com/gastownhall/gascity/internal/config" "github.com/gastownhall/gascity/internal/fsys" + "github.com/gastownhall/gascity/internal/runtime" ) func TestMCPIdentityForFilename(t *testing.T) { @@ -196,6 +197,70 @@ func TestNormalizeMCPServerStableMapOrder(t *testing.T) { } } +func TestRuntimeMCPServersPreservesTransport(t *testing.T) { + t.Parallel() + + got := RuntimeMCPServers([]MCPServer{ + {Name: "stdio", Transport: MCPTransportStdio, Command: "uvx"}, + {Name: "http", Transport: MCPTransportHTTP, URL: "https://example.test/http"}, + {Name: "sse", Transport: MCPTransportSSE, URL: "https://example.test/sse"}, + }) + want := []runtime.MCPServerConfig{ + {Name: "http", Transport: runtime.MCPTransportHTTP, URL: "https://example.test/http"}, + {Name: "sse", Transport: runtime.MCPTransportSSE, URL: "https://example.test/sse"}, + {Name: "stdio", Transport: runtime.MCPTransportStdio, Command: "uvx"}, + } + if !reflect.DeepEqual(got, want) { + t.Fatalf("RuntimeMCPServers()=%#v, want %#v", got, want) + } +} + +func TestMCPTemplateDataUsesBackingTemplateName(t *testing.T) { + t.Parallel() + + agent := &config.Agent{ + Name: "worker", + Dir: "rig-a", + Env: map[string]string{"TOKEN": "abc"}, + } + got := MCPTemplateData(&config.City{}, "/tmp/city", agent, "rig-a/worker-7", "/tmp/work") + if got["AgentName"] != "rig-a/worker-7" { + t.Fatalf("AgentName = %q, want %q", got["AgentName"], "rig-a/worker-7") + } + if got["TemplateName"] != "rig-a/worker" { + t.Fatalf("TemplateName = %q, want %q", got["TemplateName"], "rig-a/worker") + } + if got["TOKEN"] != "abc" { + t.Fatalf("TOKEN = %q, want abc", got["TOKEN"]) + } +} + +func TestMCPTemplateDataUsesPoolNameForPoolInstances(t *testing.T) { + t.Parallel() + + agent := &config.Agent{ + Name: "worker-3", + PoolName: "worker", + } + got := MCPTemplateData(&config.City{}, "/tmp/city", agent, "worker-3", "/tmp/work") + if got["TemplateName"] != "worker" { + t.Fatalf("TemplateName = %q, want %q", got["TemplateName"], "worker") + } +} + +func TestMCPTemplateDataPreservesBranchAlias(t *testing.T) { + t.Parallel() + + agent := &config.Agent{Name: "worker"} + got := MCPTemplateData(&config.City{}, "/tmp/city", agent, "worker-1", "") + if got["Branch"] == "" { + t.Fatal("Branch = empty, want default branch alias") + } + if got["Branch"] != got["DefaultBranch"] { + t.Fatalf("Branch = %q, want %q", got["Branch"], got["DefaultBranch"]) + } +} + func TestMCPPackSourcesForAgentOrdersAndDedupes(t *testing.T) { t.Parallel() diff --git a/internal/runtime/acp/acp.go b/internal/runtime/acp/acp.go index 80db580268..f3a851fd2a 100644 --- a/internal/runtime/acp/acp.go +++ b/internal/runtime/acp/acp.go @@ -67,8 +67,9 @@ type Provider struct { // Compile-time check. var ( - _ runtime.Provider = (*Provider)(nil) - _ runtime.InteractionProvider = (*Provider)(nil) + _ runtime.Provider = (*Provider)(nil) + _ runtime.InteractionProvider = (*Provider)(nil) + _ runtime.TransportCapabilityProvider = (*Provider)(nil) ) // NewProvider returns an ACP [Provider] that stores socket files in @@ -96,6 +97,12 @@ func NewProviderWithDir(dir string, cfg Config) *Provider { } } +// SupportsTransport reports whether this provider can host the requested +// session transport. +func (p *Provider) SupportsTransport(transport string) bool { + return transport == "acp" +} + // Start spawns an ACP agent process, performs the JSON-RPC handshake, and // optionally sends the initial nudge. Returns an error if a session with // that name already exists or the handshake fails. @@ -263,7 +270,7 @@ func (p *Provider) Start(ctx context.Context, name string, cfg runtime.Config) e hsTimeoutCtx, hsTimeoutCancel := context.WithTimeout(hsCtx, p.cfg.handshakeTimeout()) defer hsTimeoutCancel() - if err := p.handshake(hsTimeoutCtx, sc); err != nil { + if err := p.handshake(hsTimeoutCtx, sc, cfg.WorkDir, cfg.MCPServers); err != nil { // Handshake failed — kill the process. The monitor goroutine // handles listener/socket cleanup when the process exits. _ = stdinPipe.Close() @@ -301,7 +308,7 @@ func (p *Provider) Start(ctx context.Context, name string, cfg runtime.Config) e } // handshake performs the ACP initialize → initialized → session/new sequence. -func (p *Provider) handshake(ctx context.Context, sc *sessionConn) error { +func (p *Provider) handshake(ctx context.Context, sc *sessionConn, workDir string, mcpServers []runtime.MCPServerConfig) error { // Step 1: Send "initialize" request. initReq, _ := newInitializeRequest() ch, err := sc.sendRequest(initReq) @@ -328,7 +335,7 @@ func (p *Provider) handshake(ctx context.Context, sc *sessionConn) error { } // Step 3: Send "session/new" request. - newReq, _ := newSessionNewRequest() + newReq, _ := newSessionNewRequest(workDir, mcpServers) ch, err = sc.sendRequest(newReq) if err != nil { return fmt.Errorf("sending session/new: %w", err) diff --git a/internal/runtime/acp/acp_test.go b/internal/runtime/acp/acp_test.go index d9b5c177b0..6189bd1f7b 100644 --- a/internal/runtime/acp/acp_test.go +++ b/internal/runtime/acp/acp_test.go @@ -87,11 +87,10 @@ for line in sys.stdin: respond(msg_id, {"sessionId": session_id}) elif method == "session/prompt": params = msg.get("params", {}) - messages = params.get("messages", []) + blocks = params.get("prompt", []) text = "" - for m in messages: - for c in m.get("content", []): - text += c.get("text", "") + for b in blocks: + text += b.get("text", "") # Send update notification with echoed text notify("session/update", { "sessionId": session_id, diff --git a/internal/runtime/acp/protocol.go b/internal/runtime/acp/protocol.go index fff3d5a78c..3ebefd3e49 100644 --- a/internal/runtime/acp/protocol.go +++ b/internal/runtime/acp/protocol.go @@ -16,6 +16,7 @@ import ( "fmt" "os" "path/filepath" + "sort" "sync/atomic" "github.com/gastownhall/gascity/internal/runtime" @@ -66,7 +67,8 @@ type ServerInfo struct { // InitializeParams is the params for the "initialize" request. type InitializeParams struct { - ClientInfo ClientInfo `json:"clientInfo"` + ProtocolVersion int `json:"protocolVersion"` + ClientInfo ClientInfo `json:"clientInfo"` } // InitializeResult is the result of the "initialize" request. @@ -74,6 +76,66 @@ type InitializeResult struct { ServerInfo ServerInfo `json:"serverInfo"` } +// SessionNewParams is the params for the "session/new" request. +type SessionNewParams struct { + Cwd string `json:"cwd"` + McpServers []SessionNewMCPServer `json:"mcpServers"` +} + +// SessionNewMCPServer is the ACP wire representation of one MCP server +// attached to session/new. +type SessionNewMCPServer struct { + Name string + Transport runtime.MCPTransport + Command string + Args []string + Env []runtime.MCPKeyValue + URL string + Headers []runtime.MCPKeyValue +} + +type sessionNewMCPServerStdio struct { + Name string `json:"name"` + Command string `json:"command"` + Args []string `json:"args"` + Env []runtime.MCPKeyValue `json:"env"` +} + +type sessionNewMCPServerHTTP struct { + Type string `json:"type"` + Name string `json:"name"` + URL string `json:"url"` + Headers []runtime.MCPKeyValue `json:"headers"` +} + +// MarshalJSON emits the transport-specific ACP schema shape for one MCP +// server. Stdio omits the type discriminator per spec. +func (s SessionNewMCPServer) MarshalJSON() ([]byte, error) { + switch s.Transport { + case runtime.MCPTransportHTTP: + return json.Marshal(sessionNewMCPServerHTTP{ + Type: string(runtime.MCPTransportHTTP), + Name: s.Name, + URL: s.URL, + Headers: nonNilMCPKeyValues(s.Headers), + }) + case runtime.MCPTransportSSE: + return json.Marshal(sessionNewMCPServerHTTP{ + Type: string(runtime.MCPTransportSSE), + Name: s.Name, + URL: s.URL, + Headers: nonNilMCPKeyValues(s.Headers), + }) + default: + return json.Marshal(sessionNewMCPServerStdio{ + Name: s.Name, + Command: s.Command, + Args: nonNilStrings(s.Args), + Env: nonNilMCPKeyValues(s.Env), + }) + } +} + // SessionNewResult is the result of the "session/new" request. type SessionNewResult struct { SessionID string `json:"sessionId"` @@ -81,14 +143,8 @@ type SessionNewResult struct { // SessionPromptParams is the params for the "session/prompt" request. type SessionPromptParams struct { - SessionID string `json:"sessionId"` - Messages []PromptMessage `json:"messages"` -} - -// PromptMessage is a message within a session/prompt request. -type PromptMessage struct { - Role string `json:"role"` - Content []ContentBlock `json:"content"` + SessionID string `json:"sessionId"` + Prompt []ContentBlock `json:"prompt"` } // SessionUpdateParams is the params for "session/update" notifications. @@ -123,7 +179,8 @@ func newNotification(method string) JSONRPCMessage { // newInitializeRequest creates an "initialize" request. func newInitializeRequest() (JSONRPCMessage, int64) { return newRequest("initialize", InitializeParams{ - ClientInfo: ClientInfo{Name: "gc", Version: "1.0"}, + ProtocolVersion: 1, + ClientInfo: ClientInfo{Name: "gc", Version: "1.0"}, }) } @@ -133,8 +190,61 @@ func newInitializedNotification() JSONRPCMessage { } // newSessionNewRequest creates a "session/new" request. -func newSessionNewRequest() (JSONRPCMessage, int64) { - return newRequest("session/new", nil) +func newSessionNewRequest(workDir string, mcpServers []runtime.MCPServerConfig) (JSONRPCMessage, int64) { + return newRequest("session/new", SessionNewParams{ + Cwd: workDir, + McpServers: sessionNewMCPServers(mcpServers), + }) +} + +func sessionNewMCPServers(servers []runtime.MCPServerConfig) []SessionNewMCPServer { + if len(servers) == 0 { + return []SessionNewMCPServer{} + } + normalized := runtime.NormalizeMCPServerConfigs(servers) + out := make([]SessionNewMCPServer, 0, len(normalized)) + for _, server := range normalized { + out = append(out, SessionNewMCPServer{ + Name: server.Name, + Transport: server.Transport, + Command: server.Command, + Args: append([]string(nil), server.Args...), + Env: sortedMCPKeyValues(server.Env), + URL: server.URL, + Headers: sortedMCPKeyValues(server.Headers), + }) + } + return out +} + +func sortedMCPKeyValues(values map[string]string) []runtime.MCPKeyValue { + if len(values) == 0 { + return nil + } + keys := make([]string, 0, len(values)) + for key := range values { + keys = append(keys, key) + } + sort.Strings(keys) + out := make([]runtime.MCPKeyValue, 0, len(keys)) + for _, key := range keys { + out = append(out, runtime.MCPKeyValue{Name: key, Value: values[key]}) + } + return out +} + +func nonNilStrings(values []string) []string { + if values == nil { + return []string{} + } + return values +} + +func nonNilMCPKeyValues(values []runtime.MCPKeyValue) []runtime.MCPKeyValue { + if values == nil { + return []runtime.MCPKeyValue{} + } + return values } // newSessionPromptRequest creates a "session/prompt" request from @@ -157,12 +267,7 @@ func newSessionPromptRequest(sessionID string, content []runtime.ContentBlock) ( } return newRequest("session/prompt", SessionPromptParams{ SessionID: sessionID, - Messages: []PromptMessage{ - { - Role: "user", - Content: blocks, - }, - }, + Prompt: blocks, }) } diff --git a/internal/runtime/acp/protocol_test.go b/internal/runtime/acp/protocol_test.go index ebf09c7ebb..1fc03f5df4 100644 --- a/internal/runtime/acp/protocol_test.go +++ b/internal/runtime/acp/protocol_test.go @@ -146,14 +146,14 @@ func TestSessionPromptRequest_Structure(t *testing.T) { if params.SessionID != "sess-1" { t.Errorf("sessionId = %q, want %q", params.SessionID, "sess-1") } - if len(params.Messages) != 1 { - t.Fatalf("messages len = %d, want 1", len(params.Messages)) + if len(params.Prompt) != 1 { + t.Fatalf("prompt len = %d, want 1", len(params.Prompt)) } - if params.Messages[0].Role != "user" { - t.Errorf("role = %q, want %q", params.Messages[0].Role, "user") + if params.Prompt[0].Type != "text" { + t.Errorf("type = %q, want %q", params.Prompt[0].Type, "text") } - if len(params.Messages[0].Content) != 1 || params.Messages[0].Content[0].Text != "hello world" { - t.Errorf("content text = %q, want %q", params.Messages[0].Content[0].Text, "hello world") + if params.Prompt[0].Text != "hello world" { + t.Errorf("text = %q, want %q", params.Prompt[0].Text, "hello world") } } @@ -170,14 +170,14 @@ func TestSessionPromptRequest_MultiBlock(t *testing.T) { var params SessionPromptParams _ = json.Unmarshal(decoded.Params, ¶ms) - if len(params.Messages[0].Content) != 2 { - t.Fatalf("content blocks = %d, want 2", len(params.Messages[0].Content)) + if len(params.Prompt) != 2 { + t.Fatalf("prompt blocks = %d, want 2", len(params.Prompt)) } - if params.Messages[0].Content[0].Text != "first" { - t.Errorf("block[0] = %q, want %q", params.Messages[0].Content[0].Text, "first") + if params.Prompt[0].Text != "first" { + t.Errorf("block[0] = %q, want %q", params.Prompt[0].Text, "first") } - if params.Messages[0].Content[1].Text != "second" { - t.Errorf("block[1] = %q, want %q", params.Messages[0].Content[1].Text, "second") + if params.Prompt[1].Text != "second" { + t.Errorf("block[1] = %q, want %q", params.Prompt[1].Text, "second") } } @@ -199,10 +199,10 @@ func TestSessionPromptRequest_FilePath(t *testing.T) { var params SessionPromptParams _ = json.Unmarshal(decoded.Params, ¶ms) - if len(params.Messages[0].Content) != 1 { - t.Fatalf("content blocks = %d, want 1", len(params.Messages[0].Content)) + if len(params.Prompt) != 1 { + t.Fatalf("prompt blocks = %d, want 1", len(params.Prompt)) } - block := params.Messages[0].Content[0] + block := params.Prompt[0] if block.Type != "text" { t.Errorf("type = %q, want %q", block.Type, "text") } @@ -226,7 +226,7 @@ func TestSessionPromptRequest_FilePathError(t *testing.T) { var params SessionPromptParams _ = json.Unmarshal(decoded.Params, ¶ms) - block := params.Messages[0].Content[0] + block := params.Prompt[0] if !strings.Contains(block.Text, "Error reading") { t.Errorf("block should contain error, got %q", block.Text) } @@ -243,3 +243,189 @@ func TestNewRequest_IncrementingIDs(t *testing.T) { t.Errorf("IDs should be incrementing: %d, %d", id1, id2) } } + +func TestInitializeRequest_IncludesProtocolVersion(t *testing.T) { + msg, _ := newInitializeRequest() + data, err := json.Marshal(msg) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + + // Verify raw JSON contains protocolVersion (not omitted via omitempty). + if !strings.Contains(string(data), `"protocolVersion":1`) { + t.Errorf("raw JSON should contain \"protocolVersion\":1, got %s", data) + } + + var decoded JSONRPCMessage + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + + var params InitializeParams + if err := json.Unmarshal(decoded.Params, ¶ms); err != nil { + t.Fatalf("Unmarshal params: %v", err) + } + if params.ProtocolVersion != 1 { + t.Errorf("protocolVersion = %d, want 1", params.ProtocolVersion) + } +} + +func TestSessionNewRequest_IncludesCwdAndMcpServers(t *testing.T) { + msg, _ := newSessionNewRequest("/home/user/project", nil) + data, err := json.Marshal(msg) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + + var decoded JSONRPCMessage + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + + if decoded.Method != "session/new" { + t.Errorf("method = %q, want %q", decoded.Method, "session/new") + } + + var params SessionNewParams + if err := json.Unmarshal(decoded.Params, ¶ms); err != nil { + t.Fatalf("Unmarshal params: %v", err) + } + if params.Cwd != "/home/user/project" { + t.Errorf("cwd = %q, want %q", params.Cwd, "/home/user/project") + } + if params.McpServers == nil { + t.Fatal("mcpServers should be non-nil empty array") + } + if len(params.McpServers) != 0 { + t.Errorf("mcpServers len = %d, want 0", len(params.McpServers)) + } + // Verify raw JSON has [] not null for mcpServers. + if !strings.Contains(string(data), `"mcpServers":[]`) { + t.Errorf("raw JSON should contain \"mcpServers\":[], got %s", data) + } +} + +func TestSessionNewRequest_SerializesMCPServersByTransport(t *testing.T) { + msg, _ := newSessionNewRequest("/home/user/project", []runtime.MCPServerConfig{ + { + Name: "filesystem", + Transport: runtime.MCPTransportStdio, + Command: "/bin/mcp-fs", + Args: []string{"--stdio"}, + Env: map[string]string{ + "HOME": "/tmp/home", + "TOKEN": "secret", + }, + }, + { + Name: "remote", + Transport: runtime.MCPTransportHTTP, + URL: "https://mcp.example.test", + Headers: map[string]string{ + "Authorization": "Bearer token", + }, + }, + { + Name: "stream", + Transport: runtime.MCPTransportSSE, + URL: "https://mcp.example.test/sse", + Headers: map[string]string{ + "X-Test": "1", + }, + }, + }) + data, err := json.Marshal(msg) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + + var decoded JSONRPCMessage + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + + var params struct { + Cwd string `json:"cwd"` + McpServers []json.RawMessage `json:"mcpServers"` + } + if err := json.Unmarshal(decoded.Params, ¶ms); err != nil { + t.Fatalf("Unmarshal params: %v", err) + } + if len(params.McpServers) != 3 { + t.Fatalf("mcpServers len = %d, want 3", len(params.McpServers)) + } + + var stdio struct { + Type string `json:"type"` + Name string `json:"name"` + Command string `json:"command"` + Args []string `json:"args"` + Env []runtime.MCPKeyValue `json:"env"` + } + if err := json.Unmarshal(params.McpServers[0], &stdio); err != nil { + t.Fatalf("Unmarshal stdio server: %v", err) + } + if stdio.Type != "" { + t.Fatalf("stdio type = %q, want omitted", stdio.Type) + } + if stdio.Command != "/bin/mcp-fs" { + t.Fatalf("stdio command = %q, want /bin/mcp-fs", stdio.Command) + } + if len(stdio.Env) != 2 || stdio.Env[0].Name != "HOME" || stdio.Env[1].Name != "TOKEN" { + t.Fatalf("stdio env = %#v, want sorted HOME/TOKEN", stdio.Env) + } + + var http struct { + Type string `json:"type"` + Name string `json:"name"` + URL string `json:"url"` + Headers []runtime.MCPKeyValue `json:"headers"` + } + if err := json.Unmarshal(params.McpServers[1], &http); err != nil { + t.Fatalf("Unmarshal http server: %v", err) + } + if http.Type != "http" { + t.Fatalf("http type = %q, want http", http.Type) + } + if http.URL != "https://mcp.example.test" { + t.Fatalf("http url = %q, want https://mcp.example.test", http.URL) + } + if len(http.Headers) != 1 || http.Headers[0].Name != "Authorization" { + t.Fatalf("http headers = %#v, want Authorization", http.Headers) + } + + var sse struct { + Type string `json:"type"` + Name string `json:"name"` + URL string `json:"url"` + Headers []runtime.MCPKeyValue `json:"headers"` + } + if err := json.Unmarshal(params.McpServers[2], &sse); err != nil { + t.Fatalf("Unmarshal sse server: %v", err) + } + if sse.Type != "sse" { + t.Fatalf("sse type = %q, want sse", sse.Type) + } + if sse.URL != "https://mcp.example.test/sse" { + t.Fatalf("sse url = %q, want https://mcp.example.test/sse", sse.URL) + } + if len(sse.Headers) != 1 || sse.Headers[0].Name != "X-Test" { + t.Fatalf("sse headers = %#v, want X-Test", sse.Headers) + } +} + +func TestSessionPromptRequest_UsesPromptFieldNotMessages(t *testing.T) { + msg, _ := newSessionPromptRequest("sess-1", runtime.TextContent("test")) + data, err := json.Marshal(msg) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + + raw := string(data) + if !strings.Contains(raw, `"prompt":[`) { + t.Errorf("raw JSON should contain \"prompt\":[ field, got %s", raw) + } + if strings.Contains(raw, `"messages"`) { + t.Errorf("raw JSON should NOT contain \"messages\" field, got %s", raw) + } +} diff --git a/internal/runtime/auto/auto.go b/internal/runtime/auto/auto.go index 85456fe525..fb3bffbd2c 100644 --- a/internal/runtime/auto/auto.go +++ b/internal/runtime/auto/auto.go @@ -29,6 +29,7 @@ var ( _ runtime.InteractionProvider = (*Provider)(nil) _ runtime.InterruptBoundaryWaitProvider = (*Provider)(nil) _ runtime.InterruptedTurnResetProvider = (*Provider)(nil) + _ runtime.TransportCapabilityProvider = (*Provider)(nil) ) // New creates a composite provider. defaultSP handles sessions not @@ -67,6 +68,18 @@ func (p *Provider) route(name string) runtime.Provider { return p.defaultSP } +// SupportsTransport reports whether this provider can route the requested +// session transport. +func (p *Provider) SupportsTransport(transport string) bool { + if transport != "acp" { + return true + } + if provider, ok := p.acpSP.(runtime.TransportCapabilityProvider); ok { + return provider.SupportsTransport(transport) + } + return false +} + // DetectTransport reports the backend currently hosting the named session. // It returns "acp" for ACP-backed sessions and "" for default or unknown. func (p *Provider) DetectTransport(name string) string { diff --git a/internal/runtime/fingerprint.go b/internal/runtime/fingerprint.go index 9b8030d659..370486273f 100644 --- a/internal/runtime/fingerprint.go +++ b/internal/runtime/fingerprint.go @@ -123,6 +123,7 @@ func hashCoreFields(h hash.Hash, cfg Config) { h.Write([]byte{0}) //nolint:errcheck // hash.Write never errors hashSortedMapIncluded(h, cfg.Env, envFingerprintInclude) + hashMCPServers(h, cfg.MCPServers) // FingerprintExtra carries additional identity fields (pool config, etc.) // that aren't part of the session command but should @@ -220,6 +221,28 @@ func hashSortedMap(h hash.Hash, m map[string]string) { } } +func hashMCPServers(h hash.Hash, servers []MCPServerConfig) { + for _, server := range NormalizeMCPServerConfigs(servers) { + h.Write([]byte(server.Name)) //nolint:errcheck // hash.Write never errors + h.Write([]byte{0}) //nolint:errcheck // hash.Write never errors + h.Write([]byte(server.Transport)) //nolint:errcheck // hash.Write never errors + h.Write([]byte{0}) //nolint:errcheck // hash.Write never errors + h.Write([]byte(server.Command)) //nolint:errcheck // hash.Write never errors + h.Write([]byte{0}) //nolint:errcheck // hash.Write never errors + for _, arg := range server.Args { + h.Write([]byte(arg)) //nolint:errcheck // hash.Write never errors + h.Write([]byte{0}) //nolint:errcheck // hash.Write never errors + } + h.Write([]byte{1}) //nolint:errcheck // sentinel between args/env + hashSortedMap(h, server.Env) + h.Write([]byte{1}) //nolint:errcheck // sentinel between env/url + h.Write([]byte(server.URL)) //nolint:errcheck // hash.Write never errors + h.Write([]byte{0}) //nolint:errcheck // hash.Write never errors + hashSortedMap(h, server.Headers) + h.Write([]byte{2}) //nolint:errcheck // sentinel between servers + } +} + // CoreFingerprintBreakdown returns per-field hash components of the core // fingerprint. Used to diagnose config-drift by comparing breakdowns // from session start vs reconcile time. @@ -236,6 +259,9 @@ func CoreFingerprintBreakdown(cfg Config) map[string]string { "Env": fieldHash(func(h hash.Hash) { hashSortedMapIncluded(h, cfg.Env, envFingerprintInclude) }), + "MCPServers": fieldHash(func(h hash.Hash) { + hashMCPServers(h, cfg.MCPServers) + }), "FPExtra": fieldHash(func(h hash.Hash) { if len(cfg.FingerprintExtra) > 0 { h.Write([]byte("fp")) @@ -314,6 +340,8 @@ func LogCoreFingerprintDrift(w io.Writer, name string, storedBreakdown map[strin fmt.Fprintf(w, " Command: %q\n", current.Command) //nolint:errcheck // best-effort diag case "Env": fmt.Fprintf(w, " Env: %v\n", filteredEnv(current.Env)) //nolint:errcheck // best-effort diag + case "MCPServers": + fmt.Fprintf(w, " MCPServers: %+v\n", NormalizeMCPServerConfigs(current.MCPServers)) //nolint:errcheck // best-effort diag case "FPExtra": fmt.Fprintf(w, " FPExtra: %v (len=%d)\n", current.FingerprintExtra, len(current.FingerprintExtra)) //nolint:errcheck // best-effort diag case "PreStart": diff --git a/internal/runtime/fingerprint_test.go b/internal/runtime/fingerprint_test.go index 088e5d70b0..59e266a19b 100644 --- a/internal/runtime/fingerprint_test.go +++ b/internal/runtime/fingerprint_test.go @@ -201,6 +201,42 @@ func TestConfigFingerprintExtraDifferentValues(t *testing.T) { } } +func TestConfigFingerprintIncludesMCPServers(t *testing.T) { + a := Config{Command: "claude"} + b := Config{ + Command: "claude", + MCPServers: []MCPServerConfig{{ + Name: "filesystem", + Transport: MCPTransportStdio, + Command: "/bin/mcp", + Args: []string{"--stdio"}, + }}, + } + if ConfigFingerprint(a) == ConfigFingerprint(b) { + t.Error("MCPServers should change the config fingerprint") + } +} + +func TestConfigFingerprintMCPServersOrderIndependent(t *testing.T) { + a := Config{ + Command: "claude", + MCPServers: []MCPServerConfig{ + {Name: "remote", Transport: MCPTransportHTTP, URL: "https://mcp.example", Headers: map[string]string{"Authorization": "token"}}, + {Name: "filesystem", Transport: MCPTransportStdio, Command: "/bin/mcp", Args: []string{"--stdio"}, Env: map[string]string{"TOKEN": "abc"}}, + }, + } + b := Config{ + Command: "claude", + MCPServers: []MCPServerConfig{ + {Name: "filesystem", Transport: MCPTransportStdio, Command: "/bin/mcp", Args: []string{"--stdio"}, Env: map[string]string{"TOKEN": "abc"}}, + {Name: "remote", Transport: MCPTransportHTTP, URL: "https://mcp.example", Headers: map[string]string{"Authorization": "token"}}, + }, + } + if ConfigFingerprint(a) != ConfigFingerprint(b) { + t.Error("MCPServers order should not affect hash") + } +} + func TestConfigFingerprintNilVsEmptyExtra(t *testing.T) { a := Config{Command: "claude", FingerprintExtra: nil} b := Config{Command: "claude", FingerprintExtra: map[string]string{}} diff --git a/internal/runtime/mcp.go b/internal/runtime/mcp.go new file mode 100644 index 0000000000..c6db27eb6b --- /dev/null +++ b/internal/runtime/mcp.go @@ -0,0 +1,77 @@ +package runtime + +import "sort" + +// MCPTransport identifies the ACP session/new transport type for an MCP server. +type MCPTransport string + +const ( + // MCPTransportStdio launches the MCP server over stdio. + MCPTransportStdio MCPTransport = "stdio" + // MCPTransportHTTP connects to the MCP server over streamable HTTP. + MCPTransportHTTP MCPTransport = "http" + // MCPTransportSSE connects to the MCP server over SSE. + MCPTransportSSE MCPTransport = "sse" +) + +// MCPKeyValue is a name/value pair used for MCP env vars and HTTP headers. +type MCPKeyValue struct { + Name string `json:"name"` + Value string `json:"value"` +} + +// MCPServerConfig is the runtime-owned ACP session/new representation of one +// MCP server. Providers that do not speak ACP ignore this field. +type MCPServerConfig struct { + Name string + Transport MCPTransport + Command string + Args []string + Env map[string]string + URL string + Headers map[string]string +} + +// NormalizeMCPServerConfigs clones and deterministically sorts MCP server +// definitions so runtime configs are safe to retain and compare. +func NormalizeMCPServerConfigs(in []MCPServerConfig) []MCPServerConfig { + if len(in) == 0 { + return nil + } + out := make([]MCPServerConfig, len(in)) + for i, server := range in { + out[i] = MCPServerConfig{ + Name: server.Name, + Transport: server.Transport, + Command: server.Command, + Args: append([]string(nil), server.Args...), + Env: cloneRuntimeStringMap(server.Env), + URL: server.URL, + Headers: cloneRuntimeStringMap(server.Headers), + } + } + sort.Slice(out, func(i, j int) bool { + if out[i].Name != out[j].Name { + return out[i].Name < out[j].Name + } + if out[i].Transport != out[j].Transport { + return out[i].Transport < out[j].Transport + } + if out[i].Command != out[j].Command { + return out[i].Command < out[j].Command + } + return out[i].URL < out[j].URL + }) + return out +} + +func cloneRuntimeStringMap(in map[string]string) map[string]string { + if len(in) == 0 { + return nil + } + out := make(map[string]string, len(in)) + for key, value := range in { + out[key] = value + } + return out +} diff --git a/internal/runtime/runtime.go b/internal/runtime/runtime.go index 23414f2097..bd1dc8ca69 100644 --- a/internal/runtime/runtime.go +++ b/internal/runtime/runtime.go @@ -236,6 +236,15 @@ type DialogProvider interface { DismissKnownDialogs(ctx context.Context, name string, timeout time.Duration) error } +// TransportCapabilityProvider is an optional extension for providers that can +// report whether they support starting sessions with a specific transport. +// +// Callers use this to fail fast when a requested transport cannot be routed by +// the active session provider before session creation starts mutating state. +type TransportCapabilityProvider interface { + SupportsTransport(transport string) bool +} + // ImmediateNudgeProvider is an optional extension for runtimes that can inject // input immediately without performing their own wait-idle heuristic first. type ImmediateNudgeProvider interface { @@ -352,6 +361,10 @@ type Config struct { // Env is additional environment variables set in the session. Env map[string]string + // MCPServers is the effective ACP session/new MCP server list for this + // session. Non-ACP providers ignore it. + MCPServers []MCPServerConfig + // Startup reliability hints (all optional — zero values skip). // ReadyPromptPrefix is the prompt prefix for readiness detection (e.g. "> "). diff --git a/internal/runtime/tmux/startup_test.go b/internal/runtime/tmux/startup_test.go index 8add1e286d..da2996c252 100644 --- a/internal/runtime/tmux/startup_test.go +++ b/internal/runtime/tmux/startup_test.go @@ -663,6 +663,45 @@ func TestDoStartSession_ProcessNamesAndReadyPrefix(t *testing.T) { }) } +func TestDoStartSession_CursorReadinessHintsTriggerRuntimeWait(t *testing.T) { + ops := &fakeStartOps{ + hasSessionResult: true, + } + + cfg := runtime.Config{ + Command: "cursor-agent", + ProcessNames: []string{"cursor-agent"}, + ReadyPromptPrefix: "\u2192 ", + ReadyDelayMs: 10000, + } + + err := doStartSession(context.Background(), ops, "test", cfg, DefaultConfig().SetupTimeout) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + assertCallSequence(t, ops, []string{ + "createSession", + "setRemainOnExit", + "waitForCommand", + "acceptStartupDialogs", + "waitForReady", + "acceptStartupDialogs", + "hasSession", + }) + + wfr := ops.calls[4] + if wfr.rc.Tmux.ReadyPromptPrefix != "\u2192 " { + t.Errorf("rc.ReadyPromptPrefix = %q, want %q", wfr.rc.Tmux.ReadyPromptPrefix, "\u2192 ") + } + if wfr.rc.Tmux.ReadyDelayMs != 10000 { + t.Errorf("rc.ReadyDelayMs = %d, want %d", wfr.rc.Tmux.ReadyDelayMs, 10000) + } + if len(wfr.rc.Tmux.ProcessNames) != 1 || wfr.rc.Tmux.ProcessNames[0] != "cursor-agent" { + t.Errorf("rc.ProcessNames = %v, want [cursor-agent]", wfr.rc.Tmux.ProcessNames) + } +} + func TestDoStartSession_ProcessNamesAndReadyDelayRechecksDialogs(t *testing.T) { ops := &fakeStartOps{ hasSessionResult: true, diff --git a/internal/session/chat.go b/internal/session/chat.go index 91f8e767bf..4884eae018 100644 --- a/internal/session/chat.go +++ b/internal/session/chat.go @@ -295,6 +295,9 @@ func (m *Manager) ensureRunning(ctx context.Context, id string, b beads.Bead, se if b.Metadata["transport"] == "" && (started || transportVerified) { m.persistTransport(id, b.Metadata["provider"], transport) } + if err := m.syncStoredMCPServers(id, &b, cfg.MCPServers); err != nil { + return fmt.Errorf("%w: %w", ErrStateSync, err) + } if err := m.confirmLiveSessionState(id, &b); err != nil { if started && !errors.Is(err, ErrStateSync) { _ = m.sp.Stop(sessName) diff --git a/internal/session/manager.go b/internal/session/manager.go index b2e70d00f7..8cb63548b2 100644 --- a/internal/session/manager.go +++ b/internal/session/manager.go @@ -64,7 +64,9 @@ type Info struct { Closed bool Title string Alias string + AgentName string // persisted concrete identity for MCP materialization Provider string + Transport string Command string // resolved command stored at creation WorkDir string SessionName string // tmux session name @@ -119,7 +121,7 @@ type Manager struct { store beads.Store sp runtime.Provider cityPath string - transportResolver func(template string) string + transportResolver func(template, provider string) transportResolution } // PruneResult reports which sessions were pruned and which queued wait nudges @@ -139,6 +141,11 @@ type transportDetector interface { DetectTransport(name string) string } +type transportResolution struct { + transport string + allowStoppedFallback bool +} + func normalizeTransport(provider, transport string) string { if transport != "" { return transport @@ -153,17 +160,39 @@ func transportFromMetadata(b beads.Bead) string { return normalizeTransport(b.Metadata["provider"], b.Metadata["transport"]) } +func (m *Manager) resolveConfiguredTransport(template, provider string) (string, bool) { + if m.transportResolver == nil { + return "", false + } + resolution := m.transportResolver(strings.TrimSpace(template), strings.TrimSpace(provider)) + return normalizeTransport(provider, resolution.transport), resolution.allowStoppedFallback +} + func (m *Manager) transportForBead(b beads.Bead, sessName string) (string, bool) { transport := transportFromMetadata(b) if transport != "" { return transport, false } + if strings.TrimSpace(b.Metadata[MCPIdentityMetadataKey]) != "" || + strings.TrimSpace(b.Metadata[MCPServersSnapshotMetadataKey]) != "" { + return "acp", false + } + if strings.TrimSpace(b.Metadata["pending_create_claim"]) == "true" { + transport, _ = m.resolveConfiguredTransport(b.Metadata["template"], b.Metadata["provider"]) + if transport != "" { + return transport, true + } + return "", false + } if detector, ok := m.sp.(transportDetector); ok { transport = normalizeTransport(b.Metadata["provider"], detector.DetectTransport(sessName)) if transport != "" { return transport, true } } + if m.sp != nil && m.sp.IsRunning(sessName) { + return "", false + } return "", false } @@ -193,9 +222,19 @@ func NewManager(store beads.Store, sp runtime.Provider) *Manager { } // NewManagerWithTransportResolver creates a Manager that can infer session -// transport from template config when older beads do not have transport metadata. -func NewManagerWithTransportResolver(store beads.Store, sp runtime.Provider, resolver func(template string) string) *Manager { - return &Manager{store: store, sp: sp, transportResolver: resolver} +// transport from template or provider config when older beads do not have +// transport metadata. +func NewManagerWithTransportResolver(store beads.Store, sp runtime.Provider, resolver func(template, provider string) string) *Manager { + return &Manager{ + store: store, + sp: sp, + transportResolver: func(template, provider string) transportResolution { + if resolver == nil { + return transportResolution{} + } + return transportResolution{transport: resolver(template, provider)} + }, + } } // NewManagerWithCityPath creates a Manager that can persist deferred submits @@ -205,10 +244,47 @@ func NewManagerWithCityPath(store beads.Store, sp runtime.Provider, cityPath str } // NewManagerWithTransportResolverAndCityPath creates a Manager that can infer -// session transport from template config and persist deferred submits into the -// city's nudge queue. -func NewManagerWithTransportResolverAndCityPath(store beads.Store, sp runtime.Provider, cityPath string, resolver func(template string) string) *Manager { - return &Manager{store: store, sp: sp, cityPath: cityPath, transportResolver: resolver} +// session transport from template or provider config and persist deferred +// submits into the city's nudge queue. +func NewManagerWithTransportResolverAndCityPath(store beads.Store, sp runtime.Provider, cityPath string, resolver func(template, provider string) string) *Manager { + return &Manager{ + store: store, + sp: sp, + cityPath: cityPath, + transportResolver: func(template, provider string) transportResolution { + if resolver == nil { + return transportResolution{} + } + return transportResolution{transport: resolver(template, provider)} + }, + } +} + +// NewManagerWithTransportPolicyResolverAndCityPath creates a Manager that can +// infer transport from config and, when the resolver marks it safe, continue +// using that transport for stopped legacy sessions without persisted +// transport metadata. +func NewManagerWithTransportPolicyResolverAndCityPath( + store beads.Store, + sp runtime.Provider, + cityPath string, + resolver func(template, provider string) (string, bool), +) *Manager { + return &Manager{ + store: store, + sp: sp, + cityPath: cityPath, + transportResolver: func(template, provider string) transportResolution { + if resolver == nil { + return transportResolution{} + } + transport, allowStoppedFallback := resolver(template, provider) + return transportResolution{ + transport: transport, + allowStoppedFallback: allowStoppedFallback, + } + }, + } } // Create creates a new chat session bead and starts the runtime session. @@ -346,6 +422,10 @@ func (m *Manager) createAliasedNamedWithTransport(ctx context.Context, alias, ex if explicitName != "" { b.Metadata["session_name_explicit"] = "true" } + if err := m.syncStoredMCPServers(b.ID, &b, hints.MCPServers); err != nil { + _ = m.store.Close(b.ID) + return err + } unroute := m.routeACPIfNeeded(provider, transport, sessName) rollbackFailedCreate := func() error { @@ -681,6 +761,7 @@ func (m *Manager) Close(id string) error { return err } if b.Status == "closed" { + _ = clearRuntimeMCPServersSnapshot(m.cityPath, id) return nil // idempotent: already closed } // CmdClose is legal from any non-none state; this is effectively a @@ -710,7 +791,11 @@ func (m *Manager) Close(id string) error { return err } - return m.store.Close(id) + if err := m.store.Close(id); err != nil { + return err + } + _ = clearRuntimeMCPServersSnapshot(m.cityPath, id) + return nil }) } @@ -1138,8 +1223,9 @@ func (m *Manager) infoFromBead(b beads.Bead) Info { sessName = sessionNameFor(b.ID) } closed := b.Status == "closed" + transport := transportFromMetadata(b) if !closed { - transport, _ := m.transportForBead(b, sessName) + transport, _ = m.transportForBead(b, sessName) _ = m.routeACPIfNeeded(b.Metadata["provider"], transport, sessName) } @@ -1159,7 +1245,9 @@ func (m *Manager) infoFromBead(b beads.Bead) Info { Closed: closed, Title: b.Title, Alias: b.Metadata["alias"], + AgentName: b.Metadata["agent_name"], Provider: b.Metadata["provider"], + Transport: transport, Command: b.Metadata["command"], WorkDir: b.Metadata["work_dir"], SessionName: sessName, diff --git a/internal/session/manager_test.go b/internal/session/manager_test.go index 21eb116f10..d88ea525ca 100644 --- a/internal/session/manager_test.go +++ b/internal/session/manager_test.go @@ -328,6 +328,28 @@ func TestCreateBeadOnly(t *testing.T) { } } +func TestGetSurfacesAgentNameMetadata(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + mgr := NewManager(store, sp) + + info, err := mgr.CreateBeadOnly("helper", "my chat", "claude", "/tmp", "claude", "", nil, ProviderResume{}) + if err != nil { + t.Fatalf("CreateBeadOnly: %v", err) + } + if err := store.SetMetadata(info.ID, "agent_name", "myrig/helper-adhoc-123"); err != nil { + t.Fatalf("SetMetadata(agent_name): %v", err) + } + + got, err := mgr.Get(info.ID) + if err != nil { + t.Fatalf("Get: %v", err) + } + if got.AgentName != "myrig/helper-adhoc-123" { + t.Fatalf("AgentName = %q, want %q", got.AgentName, "myrig/helper-adhoc-123") + } +} + func TestCreateNamedWithTransport_UsesExplicitSessionName(t *testing.T) { store := beads.NewMemStore() sp := runtime.NewFake() @@ -620,6 +642,35 @@ func TestClose(t *testing.T) { } } +func TestCloseRemovesRuntimeMCPSnapshot(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + cityPath := t.TempDir() + mgr := NewManagerWithCityPath(store, sp, cityPath) + + info, err := mgr.Create(context.Background(), "helper", "", "claude", "/tmp", "claude", nil, ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("Create: %v", err) + } + if err := PersistRuntimeMCPServersSnapshot(cityPath, info.ID, []runtime.MCPServerConfig{{ + Name: "identity", + Transport: runtime.MCPTransportHTTP, + URL: "https://example.invalid/mcp", + }}); err != nil { + t.Fatalf("PersistRuntimeMCPServersSnapshot: %v", err) + } + if _, err := os.Stat(runtimeMCPServersSnapshotPath(cityPath, info.ID)); err != nil { + t.Fatalf("Stat(runtime snapshot): %v", err) + } + + if err := mgr.Close(info.ID); err != nil { + t.Fatalf("Close: %v", err) + } + if _, err := os.Stat(runtimeMCPServersSnapshotPath(cityPath, info.ID)); !os.IsNotExist(err) { + t.Fatalf("runtime snapshot still exists after close, stat err = %v", err) + } +} + func TestClose_ConfiguredNamedSessionRetiresIdentifiers(t *testing.T) { store := beads.NewMemStore() sp := runtime.NewFake() @@ -2089,7 +2140,7 @@ func TestSendBackfillsTransportForLegacyACPSession(t *testing.T) { t.Fatalf("Start ACP session: %v", err) } - mgr := NewManagerWithTransportResolver(store, autoSP, func(template string) string { + mgr := NewManagerWithTransportResolver(store, autoSP, func(template, _ string) string { if template == "helper" { return "acp" } @@ -2147,7 +2198,7 @@ func TestGetDoesNotPersistGuessedTransportForLegacySession(t *testing.T) { t.Fatalf("Create legacy bead: %v", err) } - mgr := NewManagerWithTransportResolver(store, autoSP, func(template string) string { + mgr := NewManagerWithTransportResolver(store, autoSP, func(template, _ string) string { if template == "helper" { return "acp" } @@ -2166,6 +2217,247 @@ func TestGetDoesNotPersistGuessedTransportForLegacySession(t *testing.T) { } } +func TestGetUsesConfiguredTransportForPendingCreateWithoutRuntimeProbe(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + + deferred, err := store.Create(beads.Bead{ + Title: "deferred acp", + Type: BeadType, + Labels: []string{ + LabelSession, + "template:helper", + }, + Metadata: map[string]string{ + "template": "helper", + "state": string(StateCreating), + "pending_create_claim": "true", + "provider": "claude", + "work_dir": "/tmp", + "command": "claude", + }, + }) + if err != nil { + t.Fatalf("Create deferred bead: %v", err) + } + + mgr := NewManagerWithTransportResolver(store, sp, func(template, _ string) string { + if template == "helper" { + return "acp" + } + return "" + }) + + info, err := mgr.Get(deferred.ID) + if err != nil { + t.Fatalf("Get: %v", err) + } + if got := info.Transport; got != "acp" { + t.Fatalf("Transport = %q, want acp", got) + } + if len(sp.Calls) != 0 { + t.Fatalf("runtime calls = %#v, want none for pending create", sp.Calls) + } +} + +func TestGetPrefersLiveTransportDetectionOverConfiguredTransportInference(t *testing.T) { + store := beads.NewMemStore() + defaultSP := runtime.NewFake() + acpSP := runtime.NewFake() + autoSP := sessionauto.New(defaultSP, acpSP) + + legacy, err := store.Create(beads.Bead{ + Title: "legacy tmux", + Type: BeadType, + Labels: []string{ + LabelSession, + "template:helper", + }, + Metadata: map[string]string{ + "template": "helper", + "state": string(StateActive), + "provider": "claude", + "work_dir": "/tmp", + "command": "claude", + }, + }) + if err != nil { + t.Fatalf("Create legacy bead: %v", err) + } + sessName := sessionNameFor(legacy.ID) + if err := store.SetMetadata(legacy.ID, "session_name", sessName); err != nil { + t.Fatalf("SetMetadata(session_name): %v", err) + } + if err := defaultSP.Start(context.Background(), sessName, runtime.Config{WorkDir: "/tmp"}); err != nil { + t.Fatalf("Start default session: %v", err) + } + + mgr := NewManagerWithTransportResolver(store, autoSP, func(template, _ string) string { + if template == "helper" { + return "acp" + } + return "" + }) + + info, err := mgr.Get(legacy.ID) + if err != nil { + t.Fatalf("Get: %v", err) + } + if got := info.Transport; got != "" { + t.Fatalf("Transport = %q, want empty for live tmux session", got) + } + + updated, err := store.Get(legacy.ID) + if err != nil { + t.Fatalf("Get updated bead: %v", err) + } + if got := updated.Metadata["transport"]; got != "" { + t.Fatalf("transport metadata = %q, want empty for live tmux session", got) + } +} + +func TestGetDoesNotInferConfiguredTransportForStoppedLegacySession(t *testing.T) { + store := beads.NewMemStore() + defaultSP := runtime.NewFake() + acpSP := runtime.NewFake() + autoSP := sessionauto.New(defaultSP, acpSP) + + legacy, err := store.Create(beads.Bead{ + Title: "legacy tmux", + Type: BeadType, + Labels: []string{ + LabelSession, + "template:helper", + }, + Metadata: map[string]string{ + "template": "helper", + "state": string(StateAsleep), + "provider": "claude", + "work_dir": "/tmp", + "command": "claude", + }, + }) + if err != nil { + t.Fatalf("Create legacy bead: %v", err) + } + sessName := sessionNameFor(legacy.ID) + if err := store.SetMetadata(legacy.ID, "session_name", sessName); err != nil { + t.Fatalf("SetMetadata(session_name): %v", err) + } + + mgr := NewManagerWithTransportResolver(store, autoSP, func(template, _ string) string { + if template == "helper" { + return "acp" + } + return "" + }) + + info, err := mgr.Get(legacy.ID) + if err != nil { + t.Fatalf("Get: %v", err) + } + if got := info.Transport; got != "" { + t.Fatalf("Transport = %q, want empty for stopped legacy session without stored transport", got) + } + + updated, err := store.Get(legacy.ID) + if err != nil { + t.Fatalf("Get updated bead: %v", err) + } + if got := updated.Metadata["transport"]; got != "" { + t.Fatalf("transport metadata = %q, want empty for read-only lookup", got) + } +} + +func TestGetDoesNotInferConfiguredTransportForStoppedLegacySessionWithPolicyFallback(t *testing.T) { + store := beads.NewMemStore() + defaultSP := runtime.NewFake() + acpSP := runtime.NewFake() + autoSP := sessionauto.New(defaultSP, acpSP) + + legacy, err := store.Create(beads.Bead{ + Title: "legacy acp", + Type: BeadType, + Labels: []string{ + LabelSession, + "template:helper", + }, + Metadata: map[string]string{ + "template": "helper", + "state": string(StateAsleep), + "provider": "claude", + "work_dir": "/tmp", + "command": "claude", + }, + }) + if err != nil { + t.Fatalf("Create legacy bead: %v", err) + } + sessName := sessionNameFor(legacy.ID) + if err := store.SetMetadata(legacy.ID, "session_name", sessName); err != nil { + t.Fatalf("SetMetadata(session_name): %v", err) + } + + mgr := NewManagerWithTransportPolicyResolverAndCityPath(store, autoSP, "", func(template, _ string) (string, bool) { + if template == "helper" { + return "acp", true + } + return "", false + }) + + info, err := mgr.Get(legacy.ID) + if err != nil { + t.Fatalf("Get: %v", err) + } + if got := info.Transport; got != "" { + t.Fatalf("Transport = %q, want empty for stopped legacy session without stored evidence", got) + } + + updated, err := store.Get(legacy.ID) + if err != nil { + t.Fatalf("Get updated bead: %v", err) + } + if got := updated.Metadata["transport"]; got != "" { + t.Fatalf("transport metadata = %q, want empty for read-only lookup", got) + } +} + +func TestGetInfersACPTransportFromStoredMCPMetadata(t *testing.T) { + store := beads.NewMemStore() + defaultSP := runtime.NewFake() + acpSP := runtime.NewFake() + autoSP := sessionauto.New(defaultSP, acpSP) + + legacy, err := store.Create(beads.Bead{ + Title: "legacy acp", + Type: BeadType, + Labels: []string{ + LabelSession, + "template:helper", + }, + Metadata: map[string]string{ + "template": "helper", + "state": string(StateAsleep), + "provider": "claude", + "work_dir": "/tmp", + "command": "claude", + MCPServersSnapshotMetadataKey: `[{"name":"filesystem","transport":"stdio","command":"/bin/mcp"}]`, + }, + }) + if err != nil { + t.Fatalf("Create legacy bead: %v", err) + } + + mgr := NewManagerWithTransportResolver(store, autoSP, nil) + info, err := mgr.Get(legacy.ID) + if err != nil { + t.Fatalf("Get: %v", err) + } + if got := info.Transport; got != "acp" { + t.Fatalf("Transport = %q, want acp from stored MCP metadata", got) + } +} + func TestSendConvergesWhenSessionAlreadyResumed(t *testing.T) { store := beads.NewMemStore() sp := runtime.NewFake() diff --git a/internal/session/mcp_metadata.go b/internal/session/mcp_metadata.go new file mode 100644 index 0000000000..a8b0812d72 --- /dev/null +++ b/internal/session/mcp_metadata.go @@ -0,0 +1,319 @@ +package session + +import ( + "encoding/json" + "fmt" + "net/url" + "strings" + + "github.com/gastownhall/gascity/internal/runtime" +) + +const ( + // MCPIdentityMetadataKey stores the stable identity used to materialize + // MCP templates for a session. + MCPIdentityMetadataKey = "mcp_identity" + // MCPServersSnapshotMetadataKey stores the normalized ACP session/new MCP + // server snapshot used to resume sessions when the current catalog cannot + // be materialized. + MCPServersSnapshotMetadataKey = "mcp_servers_snapshot" + + redactedMCPSnapshotValue = "__redacted__" +) + +// EncodeMCPServersSnapshot returns the normalized metadata value for a +// session's persisted ACP session/new MCP server snapshot. +func EncodeMCPServersSnapshot(servers []runtime.MCPServerConfig) (string, error) { + normalized := normalizeMCPServersSnapshotForMetadata(servers) + if len(normalized) == 0 { + return "", nil + } + data, err := json.Marshal(normalized) + if err != nil { + return "", fmt.Errorf("marshal MCP server snapshot: %w", err) + } + return string(data), nil +} + +// DecodeMCPServersSnapshot parses a persisted ACP session/new MCP server +// snapshot from session metadata. +func DecodeMCPServersSnapshot(raw string) ([]runtime.MCPServerConfig, error) { + raw = strings.TrimSpace(raw) + if raw == "" { + return nil, nil + } + var servers []runtime.MCPServerConfig + if err := json.Unmarshal([]byte(raw), &servers); err != nil { + return nil, fmt.Errorf("unmarshal MCP server snapshot: %w", err) + } + return runtime.NormalizeMCPServerConfigs(servers), nil +} + +// StoredMCPSnapshotContainsRedactions reports whether a decoded persisted MCP +// snapshot contains redacted secret placeholders. +func StoredMCPSnapshotContainsRedactions(servers []runtime.MCPServerConfig) bool { + for _, server := range servers { + if snapshotMapContainsRedactions(server.Env) || + snapshotMapContainsRedactions(server.Headers) || + snapshotArgsContainRedactions(server.Args) || + strings.Contains(server.URL, redactedMCPSnapshotValue) { + return true + } + } + return false +} + +// SanitizeStoredMCPSnapshotForResume strips redacted secret placeholders from +// a stored MCP snapshot while preserving any non-secret fields that can still +// help degraded resume reconstruct MCP hints. +func SanitizeStoredMCPSnapshotForResume(servers []runtime.MCPServerConfig) []runtime.MCPServerConfig { + if len(servers) == 0 { + return nil + } + normalized := runtime.NormalizeMCPServerConfigs(servers) + for i := range normalized { + normalized[i].Args = sanitizeStoredMCPMetadataArgs(normalized[i].Args) + normalized[i].Env = sanitizeStoredMCPMetadataMap(normalized[i].Env) + normalized[i].URL = sanitizeStoredMCPMetadataURL(normalized[i].URL) + normalized[i].Headers = sanitizeStoredMCPMetadataMap(normalized[i].Headers) + } + return runtime.NormalizeMCPServerConfigs(normalized) +} + +// WithStoredMCPMetadata returns a metadata map augmented with the stable MCP +// identity and normalized ACP session/new snapshot for the session. +func WithStoredMCPMetadata(meta map[string]string, identity string, servers []runtime.MCPServerConfig) (map[string]string, error) { + if meta == nil { + meta = make(map[string]string) + } + identity = strings.TrimSpace(identity) + if identity != "" { + meta[MCPIdentityMetadataKey] = identity + } + snapshot, err := EncodeMCPServersSnapshot(servers) + if err != nil { + return nil, err + } + if snapshot != "" { + meta[MCPServersSnapshotMetadataKey] = snapshot + } else if _, ok := meta[MCPServersSnapshotMetadataKey]; ok { + meta[MCPServersSnapshotMetadataKey] = "" + } + return meta, nil +} + +func normalizeMCPServersSnapshotForMetadata(servers []runtime.MCPServerConfig) []runtime.MCPServerConfig { + normalized := runtime.NormalizeMCPServerConfigs(servers) + for i := range normalized { + normalized[i].Args = redactMCPMetadataArgs(normalized[i].Args) + normalized[i].Env = redactMCPMetadataMap(normalized[i].Env) + normalized[i].URL = redactMCPMetadataURL(normalized[i].URL) + normalized[i].Headers = redactMCPMetadataMap(normalized[i].Headers) + } + return normalized +} + +func redactMCPMetadataArgs(args []string) []string { + if len(args) == 0 { + return nil + } + out := make([]string, 0, len(args)) + redactNext := false + for _, arg := range args { + if redactNext { + out = append(out, redactedMCPSnapshotValue) + redactNext = false + continue + } + if isSensitiveMCPMetadataValue(arg) { + out = append(out, redactedMCPSnapshotValue) + continue + } + if redactedURL := redactMCPMetadataURL(arg); redactedURL != arg { + out = append(out, redactedURL) + continue + } + if key, value, ok := strings.Cut(arg, "="); ok && isSensitiveMCPMetadataToken(key) { + if strings.TrimSpace(value) == "" { + out = append(out, key+"=") + } else { + out = append(out, key+"="+redactedMCPSnapshotValue) + } + continue + } + if isSensitiveMCPMetadataToken(arg) && strings.HasPrefix(strings.TrimSpace(arg), "-") { + out = append(out, arg) + redactNext = true + continue + } + out = append(out, arg) + } + return out +} + +func redactMCPMetadataMap(in map[string]string) map[string]string { + if len(in) == 0 { + return nil + } + out := make(map[string]string, len(in)) + for key := range in { + out[key] = redactedMCPSnapshotValue + } + return out +} + +func redactMCPMetadataURL(raw string) string { + raw = strings.TrimSpace(raw) + if raw == "" { + return "" + } + parsed, err := url.Parse(raw) + if err != nil { + return raw + } + changed := false + if parsed.User != nil { + if _, hasPassword := parsed.User.Password(); hasPassword { + parsed.User = url.UserPassword(redactedMCPSnapshotValue, redactedMCPSnapshotValue) + } else { + parsed.User = url.User(redactedMCPSnapshotValue) + } + changed = true + } + if query := parsed.Query(); len(query) > 0 { + for key := range query { + query.Set(key, redactedMCPSnapshotValue) + } + parsed.RawQuery = query.Encode() + changed = true + } + if !changed { + return raw + } + return parsed.String() +} + +func snapshotMapContainsRedactions(in map[string]string) bool { + for _, value := range in { + if value == redactedMCPSnapshotValue { + return true + } + } + return false +} + +func snapshotArgsContainRedactions(args []string) bool { + for _, arg := range args { + if strings.Contains(arg, redactedMCPSnapshotValue) { + return true + } + } + return false +} + +func sanitizeStoredMCPMetadataArgs(args []string) []string { + if len(args) == 0 { + return nil + } + out := make([]string, 0, len(args)) + for i := 0; i < len(args); i++ { + arg := args[i] + trimmed := strings.TrimSpace(arg) + if strings.HasPrefix(trimmed, "-") && + isSensitiveMCPMetadataToken(trimmed) && + i+1 < len(args) && + strings.Contains(args[i+1], redactedMCPSnapshotValue) { + i++ + continue + } + if !strings.Contains(arg, redactedMCPSnapshotValue) { + out = append(out, arg) + continue + } + if key, value, ok := strings.Cut(arg, "="); ok && + isSensitiveMCPMetadataToken(key) && + strings.Contains(value, redactedMCPSnapshotValue) { + continue + } + if sanitizedURL := sanitizeStoredMCPMetadataURL(arg); sanitizedURL != "" && sanitizedURL != arg { + out = append(out, sanitizedURL) + } + } + return out +} + +func sanitizeStoredMCPMetadataMap(in map[string]string) map[string]string { + if len(in) == 0 { + return nil + } + out := make(map[string]string) + for key, value := range in { + if strings.Contains(value, redactedMCPSnapshotValue) { + continue + } + out[key] = value + } + if len(out) == 0 { + return nil + } + return out +} + +func sanitizeStoredMCPMetadataURL(raw string) string { + raw = strings.TrimSpace(raw) + if raw == "" { + return "" + } + if !strings.Contains(raw, redactedMCPSnapshotValue) { + return raw + } + parsed, err := url.Parse(raw) + if err != nil { + return "" + } + if parsed.User != nil && strings.Contains(parsed.User.String(), redactedMCPSnapshotValue) { + parsed.User = nil + } + if query := parsed.Query(); len(query) > 0 { + for key, values := range query { + filtered := values[:0] + for _, value := range values { + if !strings.Contains(value, redactedMCPSnapshotValue) { + filtered = append(filtered, value) + } + } + if len(filtered) == 0 { + query.Del(key) + continue + } + query[key] = filtered + } + parsed.RawQuery = query.Encode() + } + if strings.Contains(parsed.String(), redactedMCPSnapshotValue) { + return "" + } + return parsed.String() +} + +func isSensitiveMCPMetadataToken(value string) bool { + value = strings.ToLower(strings.TrimSpace(value)) + return strings.Contains(value, "token") || + strings.Contains(value, "secret") || + strings.Contains(value, "password") || + strings.Contains(value, "passwd") || + strings.Contains(value, "authorization") || + strings.Contains(value, "auth") || + strings.Contains(value, "bearer") || + strings.Contains(value, "cookie") || + strings.Contains(value, "api-key") || + strings.Contains(value, "apikey") +} + +func isSensitiveMCPMetadataValue(value string) bool { + value = strings.ToLower(strings.TrimSpace(value)) + return strings.HasPrefix(value, "authorization:") || + strings.HasPrefix(value, "bearer ") || + strings.HasPrefix(value, "basic ") || + strings.HasPrefix(value, "token ") +} diff --git a/internal/session/mcp_metadata_test.go b/internal/session/mcp_metadata_test.go new file mode 100644 index 0000000000..eecac2d0b7 --- /dev/null +++ b/internal/session/mcp_metadata_test.go @@ -0,0 +1,151 @@ +package session + +import ( + "testing" + + "github.com/gastownhall/gascity/internal/runtime" +) + +func TestEncodeMCPServersSnapshotRedactsSecrets(t *testing.T) { + raw, err := EncodeMCPServersSnapshot([]runtime.MCPServerConfig{{ + Name: "remote", + Transport: runtime.MCPTransportHTTP, + Command: "/bin/mcp", + Args: []string{ + "--serve", + "--api-key", + "super-secret", + "--token=abc123", + "Authorization: Bearer secret", + "https://user:pass@example.invalid/mcp?token=abc123", + }, + Env: map[string]string{ + "API_TOKEN": "super-secret", + }, + URL: "https://user:pass@example.invalid/mcp?token=abc123", + Headers: map[string]string{ + "Authorization": "Bearer secret", + }, + }}) + if err != nil { + t.Fatalf("EncodeMCPServersSnapshot: %v", err) + } + + servers, err := DecodeMCPServersSnapshot(raw) + if err != nil { + t.Fatalf("DecodeMCPServersSnapshot: %v", err) + } + if len(servers) != 1 { + t.Fatalf("len(servers) = %d, want 1", len(servers)) + } + if got, want := servers[0].Env["API_TOKEN"], redactedMCPSnapshotValue; got != want { + t.Fatalf("Env[API_TOKEN] = %q, want %q", got, want) + } + if got, want := servers[0].Headers["Authorization"], redactedMCPSnapshotValue; got != want { + t.Fatalf("Headers[Authorization] = %q, want %q", got, want) + } + if got, want := servers[0].Args[0], "--serve"; got != want { + t.Fatalf("Args[0] = %q, want %q", got, want) + } + if got, want := servers[0].Args[2], redactedMCPSnapshotValue; got != want { + t.Fatalf("Args[2] = %q, want %q", got, want) + } + if got, want := servers[0].Args[3], "--token="+redactedMCPSnapshotValue; got != want { + t.Fatalf("Args[3] = %q, want %q", got, want) + } + if got, want := servers[0].Args[4], redactedMCPSnapshotValue; got != want { + t.Fatalf("Args[4] = %q, want %q", got, want) + } + if got, want := servers[0].Args[5], "https://__redacted__:__redacted__@example.invalid/mcp?token="+redactedMCPSnapshotValue; got != want { + t.Fatalf("Args[5] = %q, want %q", got, want) + } + if got, want := servers[0].URL, "https://__redacted__:__redacted__@example.invalid/mcp?token="+redactedMCPSnapshotValue; got != want { + t.Fatalf("URL = %q, want %q", got, want) + } + if !StoredMCPSnapshotContainsRedactions(servers) { + t.Fatal("StoredMCPSnapshotContainsRedactions() = false, want true") + } +} + +func TestRuntimeMCPServersSnapshotRoundTrip(t *testing.T) { + cityPath := t.TempDir() + servers := []runtime.MCPServerConfig{{ + Name: "remote", + Transport: runtime.MCPTransportHTTP, + Command: "/bin/mcp", + Args: []string{"--api-key", "super-secret"}, + Env: map[string]string{ + "API_TOKEN": "super-secret", + }, + URL: "https://user:pass@example.invalid/mcp?token=abc123", + Headers: map[string]string{ + "Authorization": "Bearer secret", + }, + }} + if err := PersistRuntimeMCPServersSnapshot(cityPath, "sess-1", servers); err != nil { + t.Fatalf("PersistRuntimeMCPServersSnapshot: %v", err) + } + + loaded, err := LoadRuntimeMCPServersSnapshot(cityPath, "sess-1") + if err != nil { + t.Fatalf("LoadRuntimeMCPServersSnapshot: %v", err) + } + if len(loaded) != 1 { + t.Fatalf("len(loaded) = %d, want 1", len(loaded)) + } + if got, want := loaded[0].Args[1], "super-secret"; got != want { + t.Fatalf("Args[1] = %q, want %q", got, want) + } + if got, want := loaded[0].Env["API_TOKEN"], "super-secret"; got != want { + t.Fatalf("Env[API_TOKEN] = %q, want %q", got, want) + } + if got, want := loaded[0].Headers["Authorization"], "Bearer secret"; got != want { + t.Fatalf("Headers[Authorization] = %q, want %q", got, want) + } +} + +func TestSanitizeStoredMCPSnapshotForResumePreservesNonSecretFields(t *testing.T) { + raw, err := EncodeMCPServersSnapshot([]runtime.MCPServerConfig{{ + Name: "remote", + Transport: runtime.MCPTransportHTTP, + Command: "/bin/mcp", + Args: []string{ + "--serve", + "--api-key", + "super-secret", + "--token=abc123", + "https://user:pass@example.invalid/mcp?token=abc123", + }, + Env: map[string]string{ + "API_TOKEN": "super-secret", + }, + URL: "https://user:pass@example.invalid/mcp?token=abc123", + Headers: map[string]string{ + "Authorization": "Bearer secret", + }, + }}) + if err != nil { + t.Fatalf("EncodeMCPServersSnapshot: %v", err) + } + stored, err := DecodeMCPServersSnapshot(raw) + if err != nil { + t.Fatalf("DecodeMCPServersSnapshot: %v", err) + } + + sanitized := SanitizeStoredMCPSnapshotForResume(stored) + if len(sanitized) != 1 { + t.Fatalf("len(sanitized) = %d, want 1", len(sanitized)) + } + if got, want := sanitized[0].Args, []string{"--serve"}; len(got) != len(want) || got[0] != want[0] { + t.Fatalf("Args = %#v, want %#v", got, want) + } + if len(sanitized[0].Env) != 0 { + t.Fatalf("Env = %#v, want empty", sanitized[0].Env) + } + if len(sanitized[0].Headers) != 0 { + t.Fatalf("Headers = %#v, want empty", sanitized[0].Headers) + } + if got, want := sanitized[0].URL, "https://example.invalid/mcp"; got != want { + t.Fatalf("URL = %q, want %q", got, want) + } +} diff --git a/internal/session/mcp_state.go b/internal/session/mcp_state.go new file mode 100644 index 0000000000..1726752944 --- /dev/null +++ b/internal/session/mcp_state.go @@ -0,0 +1,123 @@ +package session + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/gastownhall/gascity/internal/beads" + "github.com/gastownhall/gascity/internal/citylayout" + "github.com/gastownhall/gascity/internal/runtime" +) + +func (m *Manager) syncStoredMCPServers(id string, b *beads.Bead, servers []runtime.MCPServerConfig) error { + snapshot, err := EncodeMCPServersSnapshot(servers) + if err != nil { + return err + } + current := "" + if b != nil && b.Metadata != nil { + current = strings.TrimSpace(b.Metadata[MCPServersSnapshotMetadataKey]) + } + if current != snapshot { + if err := m.store.SetMetadata(id, MCPServersSnapshotMetadataKey, snapshot); err != nil { + return fmt.Errorf("storing MCP server snapshot: %w", err) + } + if b != nil { + if b.Metadata == nil { + b.Metadata = make(map[string]string) + } + b.Metadata[MCPServersSnapshotMetadataKey] = snapshot + } + } + if err := PersistRuntimeMCPServersSnapshot(m.cityPath, id, servers); err != nil { + return fmt.Errorf("storing runtime MCP server snapshot: %w", err) + } + return nil +} + +// PersistRuntimeMCPServersSnapshot stores the full normalized MCP server +// snapshot for a session in the controller-local runtime cache. The cache is +// not exposed on the bead metadata wire and is used only as a degraded resume +// fallback when the live MCP catalog cannot be materialized. +func PersistRuntimeMCPServersSnapshot(cityPath, sessionID string, servers []runtime.MCPServerConfig) error { + path := runtimeMCPServersSnapshotPath(cityPath, sessionID) + if path == "" { + return nil + } + if len(servers) == 0 { + return clearRuntimeMCPServersSnapshot(cityPath, sessionID) + } + data, err := json.Marshal(runtime.NormalizeMCPServerConfigs(servers)) + if err != nil { + return fmt.Errorf("marshal runtime MCP snapshot: %w", err) + } + if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { + return fmt.Errorf("mkdir runtime MCP snapshot dir: %w", err) + } + temp, err := os.CreateTemp(filepath.Dir(path), filepath.Base(path)+".tmp-*") + if err != nil { + return fmt.Errorf("create runtime MCP snapshot temp file: %w", err) + } + tempPath := temp.Name() + defer func() { _ = os.Remove(tempPath) }() + if err := temp.Chmod(0o600); err != nil { + _ = temp.Close() + return fmt.Errorf("chmod runtime MCP snapshot temp file: %w", err) + } + if _, err := temp.Write(data); err != nil { + _ = temp.Close() + return fmt.Errorf("write runtime MCP snapshot: %w", err) + } + if err := temp.Close(); err != nil { + return fmt.Errorf("close runtime MCP snapshot temp file: %w", err) + } + if err := os.Rename(tempPath, path); err != nil { + return fmt.Errorf("rename runtime MCP snapshot: %w", err) + } + return nil +} + +// LoadRuntimeMCPServersSnapshot loads the full normalized MCP server snapshot +// for a session from the controller-local runtime cache. It returns nil, nil +// when no cache file exists. +func LoadRuntimeMCPServersSnapshot(cityPath, sessionID string) ([]runtime.MCPServerConfig, error) { + path := runtimeMCPServersSnapshotPath(cityPath, sessionID) + if path == "" { + return nil, nil + } + data, err := os.ReadFile(path) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, fmt.Errorf("read runtime MCP snapshot: %w", err) + } + var servers []runtime.MCPServerConfig + if err := json.Unmarshal(data, &servers); err != nil { + return nil, fmt.Errorf("unmarshal runtime MCP snapshot: %w", err) + } + return runtime.NormalizeMCPServerConfigs(servers), nil +} + +func runtimeMCPServersSnapshotPath(cityPath, sessionID string) string { + cityPath = strings.TrimSpace(cityPath) + sessionID = strings.TrimSpace(sessionID) + if cityPath == "" || sessionID == "" { + return "" + } + return citylayout.RuntimePath(cityPath, "session-mcp", sessionID+".json") +} + +func clearRuntimeMCPServersSnapshot(cityPath, sessionID string) error { + path := runtimeMCPServersSnapshotPath(cityPath, sessionID) + if path == "" { + return nil + } + if err := os.Remove(path); err != nil && !os.IsNotExist(err) { + return fmt.Errorf("remove runtime MCP snapshot: %w", err) + } + return nil +} diff --git a/internal/session/names.go b/internal/session/names.go index d3a24c7283..330fff8513 100644 --- a/internal/session/names.go +++ b/internal/session/names.go @@ -13,6 +13,7 @@ import ( "sync" "syscall" + "github.com/gastownhall/gascity/internal/agent" "github.com/gastownhall/gascity/internal/beads" "github.com/gastownhall/gascity/internal/citylayout" "github.com/gastownhall/gascity/internal/config" @@ -106,6 +107,24 @@ func GenerateAdhocExplicitName(base string) (string, error) { return ValidateExplicitName(base + suffix) } +// GenerateAdhocIdentity produces a stable, MCP-safe per-session identity for +// aliasless sessions that still need a concrete unique name for templating. +func GenerateAdhocIdentity(base string) (string, error) { + token, err := GenerateSessionKey() + if err != nil { + return "", fmt.Errorf("generate adhoc identity: %w", err) + } + compact := strings.ReplaceAll(token, "-", "") + if len(compact) > 10 { + compact = compact[:10] + } + base = agent.SanitizeQualifiedNameForSession(strings.TrimSpace(base)) + if base == "" { + base = "session" + } + return base + "-adhoc-" + compact, nil +} + // ValidateAlias validates a human-chosen session alias. Empty means // "no alias". func ValidateAlias(alias string) (string, error) { diff --git a/internal/session/template_overrides.go b/internal/session/template_overrides.go new file mode 100644 index 0000000000..a5293f422d --- /dev/null +++ b/internal/session/template_overrides.go @@ -0,0 +1,26 @@ +package session + +import ( + "encoding/json" + "fmt" + "strings" +) + +// ParseTemplateOverrides decodes persisted session template_overrides metadata. +func ParseTemplateOverrides(metadata map[string]string) (map[string]string, error) { + if metadata == nil { + return nil, nil + } + raw := strings.TrimSpace(metadata["template_overrides"]) + if raw == "" { + return nil, nil + } + var overrides map[string]string + if err := json.Unmarshal([]byte(raw), &overrides); err != nil { + return nil, fmt.Errorf("unmarshal template_overrides: %w", err) + } + if len(overrides) == 0 { + return nil, nil + } + return overrides, nil +} diff --git a/internal/sling/sling.go b/internal/sling/sling.go index adc556e482..5cfd1d9d3b 100644 --- a/internal/sling/sling.go +++ b/internal/sling/sling.go @@ -6,6 +6,7 @@ package sling import ( "context" + "errors" "fmt" "io" "os" @@ -50,8 +51,11 @@ type SlingOpts struct { Nudge bool Force bool DryRun bool - ScopeKind string - ScopeRef string + // InlineText is set only by the CLI path for ad-hoc task text. API + // callers always provide explicit bead or formula references. + InlineText bool + ScopeKind string + ScopeRef string } // AgentResolver resolves an agent name to a config.Agent. @@ -103,6 +107,9 @@ type SlingDeps struct { Runner SlingRunner Store beads.Store StoreRef string + // ValidationQuerier overrides Store for existence checks when a caller has + // already resolved the bead through a narrower view. + ValidationQuerier BeadQuerier // SourceWorkflowStores lists every bead store that may contain workflow // roots for source-workflow singleton checks and recovery. SourceWorkflowStores func() ([]SourceWorkflowStore, error) @@ -185,7 +192,10 @@ type RouteOpts struct { Nudge bool Force bool DryRun bool - SkipPoke bool + // InlineText is set only by the CLI path for ad-hoc task text. API + // callers always provide explicit bead or formula references. + InlineText bool + SkipPoke bool } // FormulaOpts holds options for formula-based operations. @@ -213,6 +223,7 @@ func (s *Sling) RouteBead(_ context.Context, beadID string, target config.Agent, Force: opts.Force, SkipPoke: opts.SkipPoke, DryRun: opts.DryRun, + InlineText: opts.InlineText, }, s.deps, s.deps.Store) } @@ -264,6 +275,7 @@ func (s *Sling) ExpandConvoy(_ context.Context, convoyID string, target config.A Force: opts.Force, SkipPoke: opts.SkipPoke, DryRun: opts.DryRun, + InlineText: opts.InlineText, }, s.deps, querier) } @@ -293,6 +305,9 @@ func SlingTracef(format string, args ...any) { // FindRigByPrefix finds a rig whose effective prefix matches (case-insensitive). func FindRigByPrefix(cfg *config.City, prefix string) (config.Rig, bool) { + if cfg == nil { + return config.Rig{}, false + } lp := strings.ToLower(prefix) for _, r := range cfg.Rigs { if strings.ToLower(r.EffectivePrefix()) == lp { @@ -302,6 +317,12 @@ func FindRigByPrefix(cfg *config.City, prefix string) (config.Rig, bool) { return config.Rig{}, false } +// IsHQPrefix reports whether prefix matches the city's HQ bead prefix. +func IsHQPrefix(cfg *config.City, prefix string) bool { + prefix = strings.TrimSpace(prefix) + return cfg != nil && prefix != "" && strings.EqualFold(prefix, config.EffectiveHQPrefix(cfg)) +} + // RigDirForBead resolves the rig directory for a bead ID by extracting // the bead prefix and looking up the rig path. func RigDirForBead(cfg *config.City, beadID string) string { @@ -398,44 +419,82 @@ func RigPrefixForAgent(a config.Agent, cfg *config.City) string { // CheckCrossRig returns a warning message if a rig-scoped agent receives // a bead from a different rig. Returns "" if routing is safe. func CheckCrossRig(beadID string, a config.Agent, cfg *config.City) string { - if cfg == nil || a.Dir == "" { + err := CrossRigRouteError(beadID, a, cfg) + if err == nil { return "" } + return err.Error() +} + +// CrossRigError reports that a rig-scoped agent was asked to route a bead from +// a different rig. +type CrossRigError struct { + BeadID string + BeadPrefix string + Target string + RigPrefix string +} + +// Error returns the cross-rig routing diagnostic. +func (e *CrossRigError) Error() string { + return fmt.Sprintf("cross-rig routing — bead %s (prefix %q) → agent %s (rig prefix %q)", e.BeadID, e.BeadPrefix, e.Target, e.RigPrefix) +} + +// CrossRigRouteError returns a typed cross-rig error when routing is unsafe. +func CrossRigRouteError(beadID string, a config.Agent, cfg *config.City) *CrossRigError { + if cfg == nil || a.Dir == "" { + return nil + } bp := BeadPrefix(beadID) if bp == "" { - return "" + return nil } rp := RigPrefixForAgent(a, cfg) if rp == "" { - return "" + return nil } if strings.EqualFold(bp, rp) { - return "" + return nil + } + return &CrossRigError{ + BeadID: beadID, + BeadPrefix: bp, + Target: a.QualifiedName(), + RigPrefix: rp, } - return fmt.Sprintf("cross-rig routing — bead %s (prefix %q) → agent %s (rig prefix %q)", beadID, bp, a.QualifiedName(), rp) } -// BeadExistsInStore checks if a bead exists in the given store. -func BeadExistsInStore(store beads.Store, id string) bool { - if store == nil { - return false +// ProbeBeadInStore checks if a bead exists in the given store and surfaces +// non-not-found lookup errors. +func ProbeBeadInStore(store beads.Store, id string) (bool, error) { + return probeBeadInQuerier(store, id) +} + +func probeBeadInQuerier(querier BeadQuerier, id string) (bool, error) { + if querier == nil { + return false, fmt.Errorf("store unavailable") + } + _, err := querier.Get(id) + if err == nil { + return true, nil + } + if errors.Is(err, beads.ErrNotFound) { + return false, nil } - _, err := store.Get(id) - return err == nil + return false, err } -// LooksLikeBeadID reports whether a string looks like a bead ID. +// LooksLikeBeadID reports whether a string loosely resembles a bead ID. +// +// Deprecated: use BeadIDParts for the stricter routing heuristic. func LooksLikeBeadID(s string) bool { s = strings.TrimSpace(s) if s == "" { return false } - // Bead IDs are typically prefix-NNN or just NNN. - // They don't contain spaces, slashes, or common text punctuation. if strings.ContainsAny(s, " \t\n/\\") { return false } - // If it contains a digit, it's likely a bead ID. for _, c := range s { if c >= '0' && c <= '9' { return true @@ -444,6 +503,77 @@ func LooksLikeBeadID(s string) bool { return false } +// BeadIDParts trims surrounding whitespace and parses a bead-like string into +// prefix and base suffix, ignoring any hierarchical ".child" suffix. It +// validates the structured bead-ID shape used by the CLI's stricter routing +// heuristic. +func BeadIDParts(s string) (prefix, baseSuffix string, ok bool) { + s = strings.TrimSpace(s) + if s == "" || strings.ContainsAny(s, " \t\n") { + return "", "", false + } + i := strings.Index(s, "-") + if i <= 0 || i == len(s)-1 || strings.Count(s, "-") != 1 { + return "", "", false + } + prefix = s[:i] + for idx, c := range prefix { + if idx == 0 { + if ('A' > c || c > 'Z') && ('a' > c || c > 'z') { + return "", "", false + } + continue + } + if ('0' > c || c > '9') && ('a' > c || c > 'z') && ('A' > c || c > 'Z') { + return "", "", false + } + } + suffix := s[i+1:] + baseSuffix = suffix + if dot := strings.IndexByte(suffix, '.'); dot > 0 { + baseSuffix = suffix[:dot] + } + if baseSuffix == "" { + return "", "", false + } + for _, c := range baseSuffix { + if ('0' > c || c > '9') && ('a' > c || c > 'z') && ('A' > c || c > 'Z') { + return "", "", false + } + } + return prefix, baseSuffix, true +} + +// MissingBeadError reports that a requested bead reference did not resolve in +// the target store. +type MissingBeadError struct { + BeadID string + StoreRef string +} + +// Error returns the missing-bead diagnostic. +func (e *MissingBeadError) Error() string { + return fmt.Sprintf("bead %q not found in store %s", e.BeadID, e.StoreRef) +} + +// BeadLookupError reports an operational failure while checking whether a bead +// exists in the target store. +type BeadLookupError struct { + BeadID string + StoreRef string + Err error +} + +// Error returns the lookup-failure diagnostic. +func (e *BeadLookupError) Error() string { + return fmt.Sprintf("getting bead %q from store %s: %v", e.BeadID, e.StoreRef, e.Err) +} + +// Unwrap returns the underlying lookup failure. +func (e *BeadLookupError) Unwrap() error { + return e.Err +} + func normalizeSlingQuery(query string) string { return strings.Join(strings.Fields(query), " ") } diff --git a/internal/sling/sling_core.go b/internal/sling/sling_core.go index d9180bc36a..75868fc50c 100644 --- a/internal/sling/sling_core.go +++ b/internal/sling/sling_core.go @@ -75,15 +75,19 @@ func preflight(opts SlingOpts, deps SlingDeps, querier BeadQuerier) (SlingResult result.PoolEmpty = true } - // Cross-rig guard. - if !opts.IsFormula && !opts.Force && !opts.DryRun { - if msg := CheckCrossRig(opts.BeadOrFormula, a, deps.Cfg); msg != "" { - return result, fmt.Errorf("%s", msg) + if shouldValidateExistingBead(opts) { + if err := validateExistingBead(opts.BeadOrFormula, deps); err != nil { + return result, err + } + } + if shouldGuardCrossRig(opts) { + if err := CrossRigRouteError(opts.BeadOrFormula, a, deps.Cfg); err != nil { + return result, err } } // Pre-flight idempotency check. - if !opts.IsFormula && !opts.Force { + if shouldCheckBeadState(opts) { check := CheckBeadState(querier, opts.BeadOrFormula, a, deps) if check.Idempotent { result.Idempotent = true @@ -115,6 +119,51 @@ func preflight(opts SlingOpts, deps SlingDeps, querier BeadQuerier) (SlingResult return result, nil } +func shouldValidateExistingBead(opts SlingOpts) bool { + if opts.IsFormula || (opts.DryRun && opts.InlineText) { + return false + } + return !opts.Force || usesFormulaBackedRoute(opts) +} + +func usesFormulaBackedRoute(opts SlingOpts) bool { + return opts.OnFormula != "" || (!opts.NoFormula && opts.Target.EffectiveDefaultSlingFormula() != "") +} + +func shouldGuardCrossRig(opts SlingOpts) bool { + return !opts.IsFormula && !opts.Force && !opts.DryRun +} + +func shouldCheckBeadState(opts SlingOpts) bool { + return !opts.IsFormula && !opts.Force && (!opts.DryRun || !opts.InlineText) +} + +func validateExistingBead(beadID string, deps SlingDeps) error { + querier := deps.ValidationQuerier + if querier == nil { + querier = deps.Store + } + return validateExistingBeadInQuerier(beadID, deps.StoreRef, querier) +} + +func validateExistingBeadInQuerier(beadID, storeRef string, querier BeadQuerier) error { + storeRef = strings.TrimSpace(storeRef) + if storeRef == "" { + storeRef = "local" + } + if querier == nil { + return &BeadLookupError{BeadID: beadID, StoreRef: storeRef, Err: errors.New("store not configured")} + } + exists, err := probeBeadInQuerier(querier, beadID) + if err != nil { + return &BeadLookupError{BeadID: beadID, StoreRef: storeRef, Err: err} + } + if exists { + return nil + } + return &MissingBeadError{BeadID: beadID, StoreRef: storeRef} +} + // slingFormula handles the --formula dispatch path. func slingFormula(opts SlingOpts, deps SlingDeps) (SlingResult, error) { a := opts.Target @@ -306,25 +355,43 @@ func finalize(opts SlingOpts, deps SlingDeps, beadID, method string, result Slin // Auto-convoy. if !opts.NoConvoy && !opts.IsFormula && deps.Store != nil { - var convoyLabels []string - if opts.Owned { - convoyLabels = []string{"owned"} - } - convoy, err := deps.Store.Create(beads.Bead{ - Title: fmt.Sprintf("sling-%s", beadID), - Type: "convoy", - Labels: convoyLabels, - }) + createAutoConvoy := true + exists, err := ProbeBeadInStore(deps.Store, beadID) if err != nil { result.MetadataErrors = append(result.MetadataErrors, - fmt.Sprintf("creating auto-convoy: %v", err)) - } else { - parentID := convoy.ID - if err := deps.Store.Update(beadID, beads.UpdateOpts{ParentID: &parentID}); err != nil { + fmt.Sprintf("checking bead before auto-convoy: %v", err)) + createAutoConvoy = false + } else if !exists { + if opts.Force { result.MetadataErrors = append(result.MetadataErrors, - fmt.Sprintf("linking bead to convoy: %v", err)) + fmt.Sprintf("forced dispatch skipped missing-bead validation for %s; no local auto-convoy created", beadID)) } else { - result.ConvoyID = convoy.ID + result.MetadataErrors = append(result.MetadataErrors, + fmt.Sprintf("skipping auto-convoy: bead %s is not present in the local store", beadID)) + } + createAutoConvoy = false + } + if createAutoConvoy { + var convoyLabels []string + if opts.Owned { + convoyLabels = []string{"owned"} + } + convoy, err := deps.Store.Create(beads.Bead{ + Title: fmt.Sprintf("sling-%s", beadID), + Type: "convoy", + Labels: convoyLabels, + }) + if err != nil { + result.MetadataErrors = append(result.MetadataErrors, + fmt.Sprintf("creating auto-convoy: %v", err)) + } else { + parentID := convoy.ID + if err := deps.Store.Update(beadID, beads.UpdateOpts{ParentID: &parentID}); err != nil { + result.MetadataErrors = append(result.MetadataErrors, + fmt.Sprintf("linking bead to convoy: %v", err)) + } else { + result.ConvoyID = convoy.ID + } } } } @@ -758,11 +825,34 @@ func DoSlingBatch(opts SlingOpts, deps SlingDeps, querier BeadChildQuerier) (Sli return DoSling(opts, deps, querier) } + containerQuerier := BeadQuerier(querier) b, err := querier.Get(opts.BeadOrFormula) if err != nil { - singleOpts := opts - singleOpts.IsFormula = false - return DoSling(singleOpts, deps, querier) + if !errors.Is(err, beads.ErrNotFound) { + return SlingResult{Target: a.QualifiedName()}, &BeadLookupError{ + BeadID: opts.BeadOrFormula, + StoreRef: deps.StoreRef, + Err: err, + } + } + if selected, ok := selectedStoreContainer(opts, deps); ok { + b = selected + // The caller's querier could not see the container, so deps.Store + // becomes authoritative for both validation and child expansion. + querier = deps.Store + containerQuerier = deps.Store + } else { + singleOpts := opts + singleOpts.IsFormula = false + return DoSling(singleOpts, deps, querier) + } + } + if b.Type == "epic" || beads.IsContainerType(b.Type) { + if shouldValidateExistingBead(opts) { + if err := validateExistingBeadInQuerier(opts.BeadOrFormula, deps.StoreRef, containerQuerier); err != nil { + return SlingResult{Target: a.QualifiedName()}, err + } + } } if b.Type == "epic" { return SlingResult{}, fmt.Errorf("bead %s is an epic; first-class support is for convoys only", b.ID) @@ -771,7 +861,9 @@ func DoSlingBatch(opts SlingOpts, deps SlingDeps, querier BeadChildQuerier) (Sli if !beads.IsContainerType(b.Type) { singleOpts := opts singleOpts.IsFormula = false - return DoSling(singleOpts, deps, querier) + singleDeps := deps + singleDeps.ValidationQuerier = containerQuerier + return DoSling(singleOpts, singleDeps, querier) } children, err := querier.List(beads.ListQuery{ @@ -798,8 +890,8 @@ func DoSlingBatch(opts SlingOpts, deps SlingDeps, querier BeadChildQuerier) (Sli // Cross-rig guard on container. if !opts.Force && !opts.DryRun { - if msg := CheckCrossRig(b.ID, a, deps.Cfg); msg != "" { - return SlingResult{}, fmt.Errorf("%s", msg) + if err := CrossRigRouteError(b.ID, a, deps.Cfg); err != nil { + return SlingResult{}, err } } @@ -975,3 +1067,14 @@ func DoSlingBatch(opts SlingOpts, deps SlingDeps, querier BeadChildQuerier) (Sli } return batchResult, nil } + +func selectedStoreContainer(opts SlingOpts, deps SlingDeps) (beads.Bead, bool) { + if deps.Store == nil { + return beads.Bead{}, false + } + b, err := deps.Store.Get(opts.BeadOrFormula) + if err != nil { + return beads.Bead{}, false + } + return b, b.Type == "epic" || beads.IsContainerType(b.Type) +} diff --git a/internal/sling/sling_test.go b/internal/sling/sling_test.go index 937d532ac7..9214647645 100644 --- a/internal/sling/sling_test.go +++ b/internal/sling/sling_test.go @@ -11,8 +11,10 @@ import ( "testing" "github.com/gastownhall/gascity/internal/beads" + beadsexec "github.com/gastownhall/gascity/internal/beads/exec" "github.com/gastownhall/gascity/internal/config" "github.com/gastownhall/gascity/internal/formulatest" + "github.com/gastownhall/gascity/internal/fsys" "github.com/gastownhall/gascity/internal/runtime" "github.com/gastownhall/gascity/internal/sourceworkflow" ) @@ -32,6 +34,15 @@ type fakeRunner struct { rules []fakeRunnerRule } +type getErrStore struct { + beads.Store + err error +} + +func (s *getErrStore) Get(_ string) (beads.Bead, error) { + return beads.Bead{}, s.err +} + type closeAllFailMemStore struct { *beads.MemStore failCloseAllCalls int @@ -77,7 +88,22 @@ func (r *fakeRunner) run(dir, command string, env map[string]string) (string, er return "", nil } -func intPtr(v int) *int { return &v } +func intPtr(v int) *int { return &v } +func stringPtr(v string) *string { return &v } + +func seededStore(ids ...string) *beads.MemStore { + seed := make([]beads.Bead, 0, len(ids)) + for _, id := range ids { + seed = append(seed, beads.Bead{ + ID: id, + Title: id, + Type: "task", + Status: "open", + Metadata: map[string]string{}, + }) + } + return beads.NewMemStoreFrom(0, seed, nil) +} // testResolver implements AgentResolver for tests using exact match. type testResolver struct{} @@ -324,6 +350,7 @@ func TestDoSlingBeadToFixedAgent(t *testing.T) { a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} deps := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-42") result, err := DoSling(testOpts(a, "BL-42"), deps, nil) if err != nil { t.Fatalf("DoSling error: %v", err) @@ -346,6 +373,7 @@ func TestDoSlingSuspendedAgentWarns(t *testing.T) { a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1), Suspended: true} deps := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-42") result, err := DoSling(testOpts(a, "BL-42"), deps, nil) if err != nil { t.Fatalf("DoSling error: %v", err) @@ -363,6 +391,7 @@ func TestDoSlingRunnerError(t *testing.T) { a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} deps := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-42") _, err := DoSling(testOpts(a, "BL-42"), deps, nil) if err == nil { @@ -406,6 +435,7 @@ func TestDoSlingCrossRigBlocks(t *testing.T) { a := config.Agent{Name: "worker", Dir: "other", MaxActiveSessions: intPtr(1)} deps := testDeps(cfg, sp, runner.run) + deps.Store = seededStore("BL-42") _, err := DoSling(testOpts(a, "BL-42"), deps, nil) if err == nil { @@ -579,6 +609,7 @@ func TestDoSlingCustomSlingQueryExpandsTemplateContext(t *testing.T) { deps := testDeps(cfg, runtime.NewFake(), runner.run) deps.CityPath = cityPath deps.CityName = "" + deps.Store = seededStore("FR-99") opts := testOpts(a, "FR-99") result, err := DoSling(opts, deps, nil) if err != nil { @@ -616,6 +647,7 @@ func TestNewSlingValid(t *testing.T) { func TestSlingRouteBead(t *testing.T) { runner := newFakeRunner() deps := testDeps(&config.City{Workspace: config.Workspace{Name: "test"}}, runtime.NewFake(), runner.run) + deps.Store = seededStore("BL-42") s, err := New(deps) if err != nil { t.Fatal(err) @@ -640,6 +672,337 @@ func TestSlingRouteBead(t *testing.T) { } } +func TestSlingRouteBeadRejectsMissingBead(t *testing.T) { + runner := newFakeRunner() + deps := testDeps(&config.City{Workspace: config.Workspace{Name: "test"}}, runtime.NewFake(), runner.run) + deps.Store = beads.NewMemStore() + s, err := New(deps) + if err != nil { + t.Fatal(err) + } + + a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} + _, err = s.RouteBead(context.Background(), "BL-404", a, RouteOpts{}) + if err == nil { + t.Fatal("RouteBead error = nil, want missing bead error") + } + if !strings.Contains(err.Error(), "BL-404") || !strings.Contains(err.Error(), "not found") { + t.Fatalf("RouteBead error = %q, want missing bead diagnostic", err) + } + if len(runner.calls) != 0 { + t.Fatalf("runner calls = %#v, want none", runner.calls) + } +} + +func TestProbeBeadInStoreTreatsBackendNotFoundAsMissing(t *testing.T) { + fileStore, err := beads.OpenFileStore(fsys.OSFS{}, filepath.Join(t.TempDir(), "beads.json")) + if err != nil { + t.Fatalf("OpenFileStore: %v", err) + } + bdStore := beads.NewBdStore(t.TempDir(), func(_, _ string, _ ...string) ([]byte, error) { + return []byte(`[]`), nil + }) + execScript := filepath.Join(t.TempDir(), "beads-provider") + if err := os.WriteFile(execScript, []byte("#!/bin/sh\ncase \"$1\" in\n get) echo 'not found' >&2; exit 1 ;;\n *) exit 2 ;;\nesac\n"), 0o755); err != nil { + t.Fatalf("write exec provider: %v", err) + } + + tests := []struct { + name string + store beads.Store + }{ + {name: "mem", store: beads.NewMemStore()}, + {name: "file", store: fileStore}, + {name: "caching", store: beads.NewCachingStoreForTest(beads.NewMemStore(), nil)}, + {name: "bd", store: bdStore}, + {name: "exec", store: beadsexec.NewStore(execScript)}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + exists, err := ProbeBeadInStore(tt.store, "NOPE-1") + if err != nil { + t.Fatalf("ProbeBeadInStore error = %v, want nil", err) + } + if exists { + t.Fatal("ProbeBeadInStore exists = true, want false") + } + }) + } +} + +func TestSlingRouteBeadDryRunRejectsMissingBead(t *testing.T) { + runner := newFakeRunner() + deps := testDeps(&config.City{Workspace: config.Workspace{Name: "test"}}, runtime.NewFake(), runner.run) + deps.Store = beads.NewMemStore() + s, err := New(deps) + if err != nil { + t.Fatal(err) + } + + a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} + _, err = s.RouteBead(context.Background(), "BL-404", a, RouteOpts{DryRun: true}) + if err == nil { + t.Fatal("RouteBead dry-run error = nil, want missing bead error") + } + if !strings.Contains(err.Error(), "BL-404") || !strings.Contains(err.Error(), "not found") { + t.Fatalf("RouteBead dry-run error = %q, want missing bead diagnostic", err) + } + if len(runner.calls) != 0 { + t.Fatalf("runner calls = %#v, want none", runner.calls) + } +} + +func TestDoSlingDryRunInlineTextSkipsMissingBeadValidation(t *testing.T) { + runner := newFakeRunner() + deps := testDeps(&config.City{Workspace: config.Workspace{Name: "test"}}, runtime.NewFake(), runner.run) + deps.Store = beads.NewMemStore() + a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} + + result, err := DoSling(SlingOpts{ + Target: a, + BeadOrFormula: "write docs", + DryRun: true, + InlineText: true, + }, deps, nil) + if err != nil { + t.Fatalf("DoSling dry-run inline text: %v", err) + } + if !result.DryRun { + t.Fatalf("DryRun = false, want true") + } + if result.BeadID != "write docs" { + t.Fatalf("BeadID = %q, want inline text", result.BeadID) + } + if len(runner.calls) != 0 { + t.Fatalf("runner calls = %#v, want none", runner.calls) + } +} + +func TestDoSlingBatchValidatesContainerInQuerierStore(t *testing.T) { + runner := newFakeRunner() + deps := testDeps(&config.City{Workspace: config.Workspace{Name: "test"}}, runtime.NewFake(), runner.run) + deps.Store = beads.NewMemStore() + querier := beads.NewMemStoreFrom(0, []beads.Bead{ + {ID: "BL-1", Title: "convoy", Type: "convoy", Status: "open", Metadata: map[string]string{}}, + {ID: "BL-2", Title: "child", Type: "task", Status: "open", ParentID: "BL-1", Metadata: map[string]string{}}, + }, nil) + + a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} + result, err := DoSlingBatch(SlingOpts{Target: a, BeadOrFormula: "BL-1"}, deps, querier) + if err != nil { + t.Fatalf("DoSlingBatch: %v", err) + } + if result.Method != "batch" { + t.Fatalf("Method = %q, want batch", result.Method) + } + if result.Routed != 1 { + t.Fatalf("Routed = %d, want 1", result.Routed) + } + if len(runner.calls) != 1 { + t.Fatalf("runner calls = %#v, want one", runner.calls) + } +} + +func TestDoSlingBatchFallsBackToSelectedStoreForContainerExpansion(t *testing.T) { + runner := newFakeRunner() + deps := testDeps(&config.City{Workspace: config.Workspace{Name: "test"}}, runtime.NewFake(), runner.run) + convoy, err := deps.Store.Create(beads.Bead{Title: "convoy", Type: "convoy"}) + if err != nil { + t.Fatalf("create convoy: %v", err) + } + if _, err := deps.Store.Create(beads.Bead{Title: "child", Type: "task", Status: "open", ParentID: convoy.ID}); err != nil { + t.Fatalf("create child: %v", err) + } + wrongQuerier := beads.NewMemStore() + + a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} + result, err := DoSlingBatch(SlingOpts{Target: a, BeadOrFormula: convoy.ID}, deps, wrongQuerier) + if err != nil { + t.Fatalf("DoSlingBatch: %v", err) + } + if result.Method != "batch" { + t.Fatalf("Method = %q, want batch", result.Method) + } + if result.Routed != 1 { + t.Fatalf("Routed = %d, want 1", result.Routed) + } +} + +func TestDoSlingBatchUsesCallerQuerierChildrenWhenContainerExistsThere(t *testing.T) { + runner := newFakeRunner() + deps := testDeps(&config.City{Workspace: config.Workspace{Name: "test"}}, runtime.NewFake(), runner.run) + deps.Store = beads.NewMemStoreFrom(0, []beads.Bead{ + {ID: "BL-1", Title: "convoy", Type: "convoy", Status: "open", Metadata: map[string]string{}}, + {ID: "BL-store-only", Title: "store child", Type: "task", Status: "open", ParentID: "BL-1", Metadata: map[string]string{}}, + }, nil) + querier := beads.NewMemStoreFrom(0, []beads.Bead{ + {ID: "BL-1", Title: "convoy", Type: "convoy", Status: "open", Metadata: map[string]string{}}, + {ID: "BL-query-only", Title: "query child", Type: "task", Status: "open", ParentID: "BL-1", Metadata: map[string]string{}}, + }, nil) + + a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} + result, err := DoSlingBatch(SlingOpts{Target: a, BeadOrFormula: "BL-1"}, deps, querier) + if err != nil { + t.Fatalf("DoSlingBatch: %v", err) + } + if result.Routed != 1 { + t.Fatalf("Routed = %d, want 1", result.Routed) + } + if len(result.Children) != 1 || result.Children[0].BeadID != "BL-query-only" { + t.Fatalf("children = %#v, want caller querier child", result.Children) + } +} + +func TestDoSlingBatchRoutesNonContainerFoundInQuerierStore(t *testing.T) { + runner := newFakeRunner() + deps := testDeps(&config.City{Workspace: config.Workspace{Name: "test"}}, runtime.NewFake(), runner.run) + deps.Store = beads.NewMemStore() + querier := beads.NewMemStoreFrom(0, []beads.Bead{ + {ID: "BL-1", Title: "task", Type: "task", Status: "open", Metadata: map[string]string{}}, + }, nil) + + a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} + result, err := DoSlingBatch(SlingOpts{Target: a, BeadOrFormula: "BL-1"}, deps, querier) + if err != nil { + t.Fatalf("DoSlingBatch: %v", err) + } + if result.Method != "bead" { + t.Fatalf("Method = %q, want bead", result.Method) + } + if len(runner.calls) != 1 { + t.Fatalf("runner calls = %#v, want one", runner.calls) + } + if result.ConvoyID != "" { + t.Fatalf("ConvoyID = %q, want no local auto-convoy", result.ConvoyID) + } + if len(result.MetadataErrors) != 1 || !strings.Contains(result.MetadataErrors[0], "skipping auto-convoy") { + t.Fatalf("MetadataErrors = %#v, want auto-convoy skip warning", result.MetadataErrors) + } +} + +func TestDoSlingBatchDoesNotFallbackOnQuerierLookupError(t *testing.T) { + runner := newFakeRunner() + deps := testDeps(&config.City{Workspace: config.Workspace{Name: "test"}}, runtime.NewFake(), runner.run) + deps.StoreRef = "rig:selected" + convoy, err := deps.Store.Create(beads.Bead{Title: "convoy", Type: "convoy"}) + if err != nil { + t.Fatalf("create convoy: %v", err) + } + if _, err := deps.Store.Create(beads.Bead{Title: "child", Type: "task", Status: "open", ParentID: convoy.ID}); err != nil { + t.Fatalf("create child: %v", err) + } + querier := &getErrStore{Store: beads.NewMemStore(), err: fmt.Errorf("backend unavailable")} + + a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} + _, err = DoSlingBatch(SlingOpts{Target: a, BeadOrFormula: convoy.ID}, deps, querier) + if err == nil { + t.Fatal("DoSlingBatch error = nil, want lookup error") + } + var lookup *BeadLookupError + if !errors.As(err, &lookup) { + t.Fatalf("DoSlingBatch error = %T %[1]v, want BeadLookupError", err) + } + if len(runner.calls) != 0 { + t.Fatalf("runner calls = %#v, want none", runner.calls) + } +} + +func TestSlingRouteBeadForceAllowsMissingBead(t *testing.T) { + runner := newFakeRunner() + deps := testDeps(&config.City{Workspace: config.Workspace{Name: "test"}}, runtime.NewFake(), runner.run) + deps.Store = beads.NewMemStore() + s, err := New(deps) + if err != nil { + t.Fatal(err) + } + + a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} + result, err := s.RouteBead(context.Background(), "BL-404", a, RouteOpts{Force: true}) + if err != nil { + t.Fatalf("RouteBead force: %v", err) + } + if len(runner.calls) != 1 { + t.Fatalf("runner calls = %#v, want one call", runner.calls) + } + if len(result.MetadataErrors) != 1 || !strings.Contains(result.MetadataErrors[0], "forced dispatch skipped missing-bead validation") { + t.Fatalf("MetadataErrors = %#v, want forced missing-bead warning", result.MetadataErrors) + } + all, err := deps.Store.List(beads.ListQuery{AllowScan: true}) + if err != nil { + t.Fatalf("list beads: %v", err) + } + if len(all) != 0 { + t.Fatalf("stored beads = %#v, want no orphan auto-convoy", all) + } +} + +func TestSlingRouteDefaultFormulaForceStillRejectsMissingBead(t *testing.T) { + runner := newFakeRunner() + deps := testDeps(&config.City{Workspace: config.Workspace{Name: "test"}}, runtime.NewFake(), runner.run) + deps.Store = beads.NewMemStore() + s, err := New(deps) + if err != nil { + t.Fatal(err) + } + + a := config.Agent{Name: "mayor", DefaultSlingFormula: stringPtr("code-review"), MaxActiveSessions: intPtr(1)} + _, err = s.RouteBead(context.Background(), "BL-404", a, RouteOpts{Force: true}) + if err == nil { + t.Fatal("RouteBead force with default formula error = nil, want missing bead error") + } + var missing *MissingBeadError + if !errors.As(err, &missing) { + t.Fatalf("RouteBead force with default formula error = %T %[1]v, want MissingBeadError", err) + } + all, listErr := deps.Store.List(beads.ListQuery{AllowScan: true}) + if listErr != nil { + t.Fatalf("list beads: %v", listErr) + } + if len(all) != 0 { + t.Fatalf("stored beads = %#v, want no orphan formula state", all) + } + if len(runner.calls) != 0 { + t.Fatalf("runner calls = %#v, want none", runner.calls) + } +} + +func TestValidateExistingBeadInQuerierNilIsLookupError(t *testing.T) { + err := validateExistingBeadInQuerier("BL-42", "rig:missing", nil) + var lookup *BeadLookupError + if !errors.As(err, &lookup) { + t.Fatalf("error = %T %[1]v, want BeadLookupError", err) + } + var missing *MissingBeadError + if errors.As(err, &missing) { + t.Fatalf("error = %T %[1]v, should not report missing bead for nil store", err) + } +} + +func TestSlingRouteBeadSurfacesStoreLookupError(t *testing.T) { + runner := newFakeRunner() + deps := testDeps(&config.City{Workspace: config.Workspace{Name: "test"}}, runtime.NewFake(), runner.run) + deps.Store = &getErrStore{Store: beads.NewMemStore(), err: fmt.Errorf("backend unavailable")} + s, err := New(deps) + if err != nil { + t.Fatal(err) + } + + a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} + _, err = s.RouteBead(context.Background(), "BL-42", a, RouteOpts{}) + if err == nil { + t.Fatal("RouteBead error = nil, want store lookup failure") + } + if !strings.Contains(err.Error(), "backend unavailable") { + t.Fatalf("RouteBead error = %q, want backend failure", err) + } + if strings.Contains(err.Error(), "not found") { + t.Fatalf("RouteBead error = %q, want store failure, not not-found", err) + } + if len(runner.calls) != 0 { + t.Fatalf("runner calls = %#v, want none", runner.calls) + } +} + func TestSlingLaunchFormula(t *testing.T) { runner := newFakeRunner() cfg := &config.City{Workspace: config.Workspace{Name: "test"}} @@ -681,6 +1044,7 @@ func TestSlingRouteBeadWithTypedRouter(t *testing.T) { cfg := &config.City{Workspace: config.Workspace{Name: "test"}} deps := testDeps(cfg, runtime.NewFake(), newFakeRunner().run) deps.Router = router + deps.Store = seededStore("BL-42") s, err := New(deps) if err != nil { @@ -733,6 +1097,61 @@ func TestSlingAttachFormula(t *testing.T) { } } +func TestSlingAttachFormulaRejectsMissingBead(t *testing.T) { + runner := newFakeRunner() + cfg := &config.City{Workspace: config.Workspace{Name: "test"}} + deps := testDeps(cfg, runtime.NewFake(), runner.run) + deps.Store = beads.NewMemStore() + + s, err := New(deps) + if err != nil { + t.Fatal(err) + } + a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} + _, err = s.AttachFormula(context.Background(), "code-review", "BL-404", a, FormulaOpts{}) + if err == nil { + t.Fatal("AttachFormula error = nil, want missing bead error") + } + var missing *MissingBeadError + if !errors.As(err, &missing) { + t.Fatalf("AttachFormula error = %T %[1]v, want MissingBeadError", err) + } + if len(runner.calls) != 0 { + t.Fatalf("runner calls = %#v, want none", runner.calls) + } +} + +func TestSlingAttachFormulaForceStillRejectsMissingBead(t *testing.T) { + runner := newFakeRunner() + cfg := &config.City{Workspace: config.Workspace{Name: "test"}} + deps := testDeps(cfg, runtime.NewFake(), runner.run) + deps.Store = beads.NewMemStore() + + s, err := New(deps) + if err != nil { + t.Fatal(err) + } + a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} + _, err = s.AttachFormula(context.Background(), "code-review", "BL-404", a, FormulaOpts{Force: true}) + if err == nil { + t.Fatal("AttachFormula force error = nil, want missing bead error") + } + var missing *MissingBeadError + if !errors.As(err, &missing) { + t.Fatalf("AttachFormula force error = %T %[1]v, want MissingBeadError", err) + } + all, listErr := deps.Store.List(beads.ListQuery{AllowScan: true}) + if listErr != nil { + t.Fatalf("list beads: %v", listErr) + } + if len(all) != 0 { + t.Fatalf("stored beads = %#v, want no orphan formula state", all) + } + if len(runner.calls) != 0 { + t.Fatalf("runner calls = %#v, want none", runner.calls) + } +} + func TestSlingAttachGraphFormulaRejectsExistingLiveRoot(t *testing.T) { formulaDir := t.TempDir() if err := os.WriteFile(filepath.Join(formulaDir, "graph-work.formula.toml"), []byte(` @@ -1901,6 +2320,7 @@ func TestDoSlingPoolEmptyWarns(t *testing.T) { cfg := &config.City{Workspace: config.Workspace{Name: "test"}} a := config.Agent{Name: "pool", MaxActiveSessions: intPtr(0)} deps := testDeps(cfg, runtime.NewFake(), runner.run) + deps.Store = seededStore("BL-1") result, err := DoSling(testOpts(a, "BL-1"), deps, nil) if err != nil { t.Fatalf("DoSling: %v", err) @@ -1937,6 +2357,7 @@ func TestFinalizeNoConvoyWhenSuppressed(t *testing.T) { cfg := &config.City{Workspace: config.Workspace{Name: "test"}} a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} deps := testDeps(cfg, runtime.NewFake(), runner.run) + deps.Store = seededStore("BL-1") result, err := DoSling(SlingOpts{ Target: a, BeadOrFormula: "BL-1", NoConvoy: true, @@ -2026,6 +2447,7 @@ func TestDoSlingDryRun(t *testing.T) { cfg := &config.City{Workspace: config.Workspace{Name: "test"}} a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} deps := testDeps(cfg, runtime.NewFake(), runner.run) + deps.Store = seededStore("BL-1") result, err := DoSling(SlingOpts{ Target: a, BeadOrFormula: "BL-1", DryRun: true, @@ -2046,6 +2468,7 @@ func TestDoSlingNudgeSignal(t *testing.T) { cfg := &config.City{Workspace: config.Workspace{Name: "test"}} a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1)} deps := testDeps(cfg, runtime.NewFake(), runner.run) + deps.Store = seededStore("BL-1") result, err := DoSling(SlingOpts{ Target: a, BeadOrFormula: "BL-1", Nudge: true, @@ -2067,6 +2490,7 @@ func TestDoSlingSuspendedAgentWarnsEvenOnFailure(t *testing.T) { a := config.Agent{Name: "mayor", MaxActiveSessions: intPtr(1), Suspended: true} deps := testDeps(cfg, runtime.NewFake(), runner.run) + deps.Store = seededStore("BL-1") result, err := DoSling(testOpts(a, "BL-1"), deps, nil) if err == nil { @@ -2089,6 +2513,7 @@ func TestDoSlingNonexistentTargetFails(_ *testing.T) { } nonexistent := config.Agent{Name: "nonexistent", MaxActiveSessions: intPtr(1)} deps := testDeps(cfg, runtime.NewFake(), runner.run) + deps.Store = seededStore("BL-1") // Cross-rig and routing should still work even if agent doesn't exist in config. // The runner will fail, but the domain doesn't validate agent existence. result, err := DoSling(testOpts(nonexistent, "BL-1"), deps, nil) @@ -2107,6 +2532,7 @@ func TestDoSlingPoolEmptyWarnsOnFailure(t *testing.T) { cfg := &config.City{Workspace: config.Workspace{Name: "test"}} a := config.Agent{Name: "empty-pool", MaxActiveSessions: intPtr(0)} deps := testDeps(cfg, runtime.NewFake(), runner.run) + deps.Store = seededStore("BL-1") result, err := DoSling(testOpts(a, "BL-1"), deps, nil) if err == nil { diff --git a/internal/worker/builtin/profiles.go b/internal/worker/builtin/profiles.go index b3dbfd5821..7638b18492 100644 --- a/internal/worker/builtin/profiles.go +++ b/internal/worker/builtin/profiles.go @@ -55,6 +55,8 @@ type BuiltinProviderSpec struct { OptionsSchema []BuiltinProviderOption PrintArgs []string TitleModel string + ACPCommand string + ACPArgs []string } // ProfileIdentity captures the explicit production identity for a canonical @@ -149,6 +151,7 @@ var builtinProviderSpecs = map[string]BuiltinProviderSpec{ Command: "codex", OptionDefaults: map[string]string{ "permission_mode": "unrestricted", + "model": "gpt-5.5", "effort": "xhigh", }, PromptMode: "arg", @@ -184,6 +187,7 @@ var builtinProviderSpecs = map[string]BuiltinProviderSpec{ Type: "select", Choices: []BuiltinOptionChoice{ {Value: "", Label: "Default"}, + {Value: "gpt-5.5", Label: "GPT-5.5", FlagArgs: []string{"--model", "gpt-5.5"}}, {Value: "o3", Label: "o3", FlagArgs: []string{"--model", "o3"}}, {Value: "o4-mini", Label: "o4-mini", FlagArgs: []string{"--model", "o4-mini"}}, }, @@ -260,13 +264,15 @@ var builtinProviderSpecs = map[string]BuiltinProviderSpec{ }, }, "cursor": { - DisplayName: "Cursor Agent", - Command: "cursor-agent", - Args: []string{"-f"}, - PromptMode: "arg", - ProcessNames: []string{"cursor-agent"}, - SupportsHooks: true, - InstructionsFile: "AGENTS.md", + DisplayName: "Cursor Agent", + Command: "cursor-agent", + Args: []string{"-f"}, + PromptMode: "arg", + ReadyPromptPrefix: "\u2192 ", + ReadyDelayMs: 10000, + ProcessNames: []string{"cursor-agent"}, + SupportsHooks: true, + InstructionsFile: "AGENTS.md", }, "copilot": { DisplayName: "GitHub Copilot", @@ -298,6 +304,7 @@ var builtinProviderSpecs = map[string]BuiltinProviderSpec{ SupportsACP: true, SupportsHooks: true, InstructionsFile: "AGENTS.md", + ACPArgs: []string{"acp"}, }, "auggie": { DisplayName: "Auggie CLI", @@ -385,6 +392,7 @@ func cloneBuiltinProviderSpec(spec BuiltinProviderSpec) BuiltinProviderSpec { spec.OptionDefaults = cloneStringMap(spec.OptionDefaults) spec.PrintArgs = cloneStrings(spec.PrintArgs) spec.OptionsSchema = cloneBuiltinOptions(spec.OptionsSchema) + spec.ACPArgs = cloneStrings(spec.ACPArgs) return spec } diff --git a/internal/worker/factory.go b/internal/worker/factory.go index 6d229603bb..26ff7afc39 100644 --- a/internal/worker/factory.go +++ b/internal/worker/factory.go @@ -13,7 +13,7 @@ import ( // SessionRuntimeResolver resolves provider/runtime details for an existing // session-backed worker without exposing SessionSpec mutation to callers. -type SessionRuntimeResolver func(info sessionpkg.Info, sessionKind string) (*ResolvedRuntime, error) +type SessionRuntimeResolver func(info sessionpkg.Info, sessionKind string, metadata map[string]string) (*ResolvedRuntime, error) // FactoryConfig constructs worker-owned session handles and catalogs without // leaking session.Manager setup into higher layers. @@ -23,7 +23,7 @@ type FactoryConfig struct { CityPath string SearchPaths []string Recorder events.Recorder - ResolveTransport func(template string) string + ResolveTransport func(template, provider string) string ResolveSessionRuntime SessionRuntimeResolver } @@ -44,7 +44,12 @@ func NewFactory(cfg FactoryConfig) (*Factory, error) { var manager *sessionpkg.Manager switch { case cfg.ResolveTransport != nil: - manager = sessionpkg.NewManagerWithTransportResolverAndCityPath(cfg.Store, cfg.Provider, cfg.CityPath, cfg.ResolveTransport) + manager = sessionpkg.NewManagerWithTransportResolverAndCityPath( + cfg.Store, + cfg.Provider, + cfg.CityPath, + cfg.ResolveTransport, + ) case cfg.CityPath != "": manager = sessionpkg.NewManagerWithCityPath(cfg.Store, cfg.Provider, cfg.CityPath) default: @@ -113,16 +118,18 @@ func (f *Factory) SessionByID(id string) (Handle, error) { }, } sessionKind := "" + var metadata map[string]string if f.store != nil { if bead, beadErr := f.store.Get(id); beadErr == nil { sessionKind = strings.TrimSpace(bead.Metadata["mc_session_kind"]) if profile := strings.TrimSpace(bead.Metadata["worker_profile"]); profile != "" { spec.Profile = Profile(profile) } + metadata = cloneStringMap(bead.Metadata) } } if f.resolveSessionRuntime != nil { - resolved, err := f.resolveSessionRuntime(info, sessionKind) + resolved, err := f.resolveSessionRuntime(info, sessionKind, metadata) if err != nil { return nil, err } diff --git a/internal/worker/factory_test.go b/internal/worker/factory_test.go index d1baf55d8b..d58b8523a3 100644 --- a/internal/worker/factory_test.go +++ b/internal/worker/factory_test.go @@ -148,7 +148,7 @@ func TestFactorySessionByIDResolvesSessionRuntime(t *testing.T) { factory, err := NewFactory(FactoryConfig{ Store: store, Provider: sp, - ResolveSessionRuntime: func(_ sessionpkg.Info, sessionKind string) (*ResolvedRuntime, error) { + ResolveSessionRuntime: func(_ sessionpkg.Info, sessionKind string, _ map[string]string) (*ResolvedRuntime, error) { gotSessionKind = sessionKind return &ResolvedRuntime{ Command: "/bin/echo", @@ -205,6 +205,64 @@ func TestFactorySessionByIDResolvesSessionRuntime(t *testing.T) { } } +func TestFactoryTransportResolverReceivesProviderForLegacyProviderSession(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + manager := sessionpkg.NewManager(store, sp) + + info, err := manager.CreateBeadOnly( + "opencode", + "Probe", + "", + t.TempDir(), + "opencode", + "", + nil, + sessionpkg.ProviderResume{}, + ) + if err != nil { + t.Fatalf("CreateBeadOnly: %v", err) + } + if err := store.SetMetadata(info.ID, "mc_session_kind", "provider"); err != nil { + t.Fatalf("SetMetadata(mc_session_kind): %v", err) + } + + var gotTemplate, gotProvider string + factory, err := NewFactory(FactoryConfig{ + Store: store, + Provider: sp, + ResolveTransport: func(template, provider string) string { + gotTemplate = template + gotProvider = provider + if provider == "opencode" { + return "acp" + } + return "" + }, + }) + if err != nil { + t.Fatalf("NewFactory: %v", err) + } + + catalog, err := factory.Catalog() + if err != nil { + t.Fatalf("Catalog: %v", err) + } + got, err := catalog.Get(info.ID) + if err != nil { + t.Fatalf("catalog.Get(%q): %v", info.ID, err) + } + if gotTemplate != "opencode" { + t.Fatalf("ResolveTransport template = %q, want %q", gotTemplate, "opencode") + } + if gotProvider != "opencode" { + t.Fatalf("ResolveTransport provider = %q, want %q", gotProvider, "opencode") + } + if got.Transport != "acp" { + t.Fatalf("catalog.Get(%q).Transport = %q, want %q", info.ID, got.Transport, "acp") + } +} + func TestFactorySessionByIDPropagatesResolvedRuntimeError(t *testing.T) { store := beads.NewMemStore() sp := runtime.NewFake() @@ -228,7 +286,7 @@ func TestFactorySessionByIDPropagatesResolvedRuntimeError(t *testing.T) { factory, err := NewFactory(FactoryConfig{ Store: store, Provider: sp, - ResolveSessionRuntime: func(sessionpkg.Info, string) (*ResolvedRuntime, error) { + ResolveSessionRuntime: func(sessionpkg.Info, string, map[string]string) (*ResolvedRuntime, error) { return nil, wantErr }, }) diff --git a/internal/worker/handle_clone.go b/internal/worker/handle_clone.go index 9a025c2a8e..4bbf15c13f 100644 --- a/internal/worker/handle_clone.go +++ b/internal/worker/handle_clone.go @@ -44,6 +44,7 @@ func mergeStringMaps(base, extra map[string]string) map[string]string { func cloneRuntimeConfig(cfg runtime.Config) runtime.Config { cfg.Env = cloneStringMap(cfg.Env) + cfg.MCPServers = runtime.NormalizeMCPServerConfigs(cfg.MCPServers) cfg.ProcessNames = append([]string(nil), cfg.ProcessNames...) cfg.PreStart = append([]string(nil), cfg.PreStart...) cfg.SessionSetup = append([]string(nil), cfg.SessionSetup...) diff --git a/test/integration/gc_live_contract_test.go b/test/integration/gc_live_contract_test.go new file mode 100644 index 0000000000..77c1a7e3e1 --- /dev/null +++ b/test/integration/gc_live_contract_test.go @@ -0,0 +1,734 @@ +//go:build integration + +package integration + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "testing" + "time" + + "github.com/gastownhall/gascity/internal/beads" + "github.com/pb33f/libopenapi" + openapivalidator "github.com/pb33f/libopenapi-validator" +) + +// TestGCLiveContract_BeadsAndEvents ports the MC live GC contract's +// API-only coverage into this repo. It boots a real supervisor, creates an +// isolated city and rig through the HTTP API, validates responses against the +// live OpenAPI document, exercises the bead lifecycle MC depends on, validates +// city and supervisor event list schemas, and unregisters the city through the +// API. +func TestGCLiveContract_BeadsAndEvents(t *testing.T) { + bin := buildGCBinary(t) + + root := shortTempDir(t) + gcHome := filepath.Join(root, "home") + runtimeDir := filepath.Join(root, "run") + for _, dir := range []string{gcHome, runtimeDir} { + if err := os.MkdirAll(dir, 0o755); err != nil { + t.Fatalf("mkdir %s: %v", dir, err) + } + } + if err := seedDoltIdentityForRoot(gcHome); err != nil { + t.Fatalf("seed dolt identity: %v", err) + } + port := reserveFreePort(t) + writeSupervisorConfig(t, gcHome, port) + + baseURL := "http://127.0.0.1:" + strconv.Itoa(port) + env := integrationEnvFor(gcHome, runtimeDir, true) + + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + cmd := exec.CommandContext(ctx, bin, "supervisor", "run") + cmd.Env = env + stderr, err := cmd.StderrPipe() + if err != nil { + t.Fatalf("stderr pipe: %v", err) + } + if err := cmd.Start(); err != nil { + t.Fatalf("start supervisor: %v", err) + } + var supervisorLog strings.Builder + go func() { _, _ = io.Copy(&supervisorLog, stderr) }() + t.Cleanup(func() { + cancel() + _ = cmd.Wait() + if t.Failed() { + t.Logf("supervisor stderr:\n%s", supervisorLog.String()) + } + }) + + waitHTTP(t, baseURL+"/health", 10*time.Second) + + specBytes := liveContractRequest(t, baseURL, nil, http.MethodGet, "/openapi.json", nil, http.StatusOK) + assertLiveContractSpec(t, specBytes) + validator := liveContractValidator(t, specBytes) + + cityName := "mc-live-contract-" + strconv.FormatInt(time.Now().UnixNano(), 36) + cityDir := filepath.Join(root, "cities", cityName) + createCity := liveContractJSON[struct { + OK bool `json:"ok"` + Name string `json:"name"` + Path string `json:"path"` + }](t, baseURL, validator, http.MethodPost, "/v0/city", map[string]string{ + "dir": cityDir, + "provider": "claude", + }, http.StatusAccepted) + if !createCity.OK || createCity.Name != cityName || createCity.Path != cityDir { + t.Fatalf("city create response = %+v, want ok=true name=%q path=%q", createCity, cityName, cityDir) + } + + cityBase := "/v0/city/" + url.PathEscape(cityName) + waitForLiveContractEvent(t, baseURL, validator, "/v0/events", cityName, "city.ready", 120*time.Second) + liveContractJSON[struct { + Status string `json:"status"` + }](t, baseURL, validator, http.MethodGet, cityBase+"/health", nil, http.StatusOK) + assertLiveContractStreamOpens(t, baseURL, "/v0/events/stream") + assertLiveContractStreamOpens(t, baseURL, cityBase+"/events/stream") + + rigName := "alpha" + rigDir := filepath.Join(cityDir, rigName) + if err := os.MkdirAll(rigDir, 0o755); err != nil { + t.Fatalf("mkdir rig: %v", err) + } + liveContractJSON[struct { + Status string `json:"status"` + Rig string `json:"rig"` + }](t, baseURL, validator, http.MethodPost, cityBase+"/rigs", map[string]string{ + "name": rigName, + "path": rigDir, + "prefix": "mc-" + strconv.FormatInt(time.Now().UnixNano(), 36), + }, http.StatusCreated) + waitForLiveContractRig(t, baseURL, validator, cityBase, rigName, rigDir, 30*time.Second) + + runID := strconv.FormatInt(time.Now().UnixNano(), 36) + rootBead := liveContractJSON[beads.Bead](t, baseURL, validator, http.MethodPost, cityBase+"/beads", map[string]any{ + "description": "Root fixture created by TestGCLiveContract_BeadsAndEvents", + "labels": []string{"mc-live-contract", "root"}, + "metadata": map[string]string{ + "mc.contract.role": "root", + "mc.contract.run_id": runID, + }, + "priority": 2, + "rig": rigName, + "title": "MC live contract root " + runID, + "type": "feature", + }, http.StatusCreated) + if rootBead.ID == "" || rootBead.Status != "open" || rootBead.Type != "feature" { + t.Fatalf("root bead = %+v, want id, open status, feature type", rootBead) + } + if rootBead.Metadata["mc.contract.run_id"] != runID { + t.Fatalf("root metadata = %#v, want run_id=%q", rootBead.Metadata, runID) + } + + liveContractJSON[struct { + Status string `json:"status"` + }](t, baseURL, validator, http.MethodPost, cityBase+"/bead/"+url.PathEscape(rootBead.ID)+"/update", map[string]any{ + "metadata": map[string]string{ + "mc.contract.metadata_update": "true", + "mc_permission_mode": "default", + "mc_starred": "true", + }, + "status": "in_progress", + }, http.StatusOK) + updatedRoot := liveContractJSON[beads.Bead](t, baseURL, validator, http.MethodGet, cityBase+"/bead/"+url.PathEscape(rootBead.ID), nil, http.StatusOK) + if updatedRoot.Status != "in_progress" || updatedRoot.Metadata["mc.contract.metadata_update"] != "true" { + t.Fatalf("updated root = %+v, want in_progress plus metadata update", updatedRoot) + } + + liveContractJSON[struct { + Status string `json:"status"` + }](t, baseURL, validator, http.MethodPost, cityBase+"/bead/"+url.PathEscape(rootBead.ID)+"/close", nil, http.StatusOK) + closedRoot := liveContractJSON[beads.Bead](t, baseURL, validator, http.MethodGet, cityBase+"/bead/"+url.PathEscape(rootBead.ID), nil, http.StatusOK) + if closedRoot.Status != "closed" { + t.Fatalf("closed root status = %q, want closed", closedRoot.Status) + } + liveContractJSON[struct { + Status string `json:"status"` + }](t, baseURL, validator, http.MethodPost, cityBase+"/bead/"+url.PathEscape(rootBead.ID)+"/reopen", nil, http.StatusOK) + reopenedRoot := liveContractJSON[beads.Bead](t, baseURL, validator, http.MethodGet, cityBase+"/bead/"+url.PathEscape(rootBead.ID), nil, http.StatusOK) + if reopenedRoot.Status != "open" { + t.Fatalf("reopened root status = %q, want open", reopenedRoot.Status) + } + + childBead := liveContractJSON[beads.Bead](t, baseURL, validator, http.MethodPost, cityBase+"/beads", map[string]any{ + "description": "Child fixture that exercises parent and update semantics", + "labels": []string{"mc-live-contract", "child", "needs-update"}, + "metadata": map[string]string{ + "mc.contract.role": "child", + "mc.contract.run_id": runID, + }, + "parent": rootBead.ID, + "priority": 1, + "rig": rigName, + "title": "MC live contract child " + runID, + "type": "task", + }, http.StatusCreated) + siblingBead := liveContractJSON[beads.Bead](t, baseURL, validator, http.MethodPost, cityBase+"/beads", map[string]any{ + "description": "Sibling fixture for list and filter coverage", + "labels": []string{"mc-live-contract", "sibling"}, + "metadata": map[string]string{ + "mc.contract.role": "sibling", + "mc.contract.run_id": runID, + }, + "parent": rootBead.ID, + "priority": 3, + "rig": rigName, + "title": "MC live contract sibling " + runID, + "type": "bug", + }, http.StatusCreated) + if childBead.ParentID != rootBead.ID || childBead.Type != "task" { + t.Fatalf("child bead = %+v, want parent=%q type=task", childBead, rootBead.ID) + } + if siblingBead.ParentID != rootBead.ID || siblingBead.Type != "bug" { + t.Fatalf("sibling bead = %+v, want parent=%q type=bug", siblingBead, rootBead.ID) + } + + liveContractJSON[struct { + Status string `json:"status"` + }](t, baseURL, validator, http.MethodPost, cityBase+"/bead/"+url.PathEscape(childBead.ID)+"/update", map[string]any{ + "description": "Updated child fixture", + "labels": []string{"verified"}, + "metadata": map[string]string{"mc.contract.updated": "true"}, + "parent": "", + "priority": 4, + "remove_labels": []string{"needs-update"}, + "status": "in_progress", + "title": "MC live contract child updated " + runID, + "type": "bug", + }, http.StatusOK) + updatedChild := liveContractJSON[beads.Bead](t, baseURL, validator, http.MethodGet, cityBase+"/bead/"+url.PathEscape(childBead.ID), nil, http.StatusOK) + if updatedChild.ParentID != "" || updatedChild.Status != "in_progress" || updatedChild.Type != "bug" || updatedChild.Priority == nil || *updatedChild.Priority != 4 { + t.Fatalf("updated child = %+v, want cleared parent, in_progress, bug, priority 4", updatedChild) + } + if !containsString(updatedChild.Labels, "verified") || containsString(updatedChild.Labels, "needs-update") { + t.Fatalf("updated child labels = %#v, want verified without needs-update", updatedChild.Labels) + } + if updatedChild.Metadata["mc.contract.updated"] != "true" { + t.Fatalf("updated child metadata = %#v, want mc.contract.updated=true", updatedChild.Metadata) + } + + liveContractJSON[struct { + Status string `json:"status"` + }](t, baseURL, validator, http.MethodPost, cityBase+"/bead/"+url.PathEscape(childBead.ID)+"/update", map[string]any{ + "metadata": map[string]string{"mc.contract.parent_restored": "true"}, + "parent": rootBead.ID, + }, http.StatusOK) + restoredChild := liveContractJSON[beads.Bead](t, baseURL, validator, http.MethodGet, cityBase+"/bead/"+url.PathEscape(childBead.ID), nil, http.StatusOK) + if restoredChild.ParentID != rootBead.ID { + t.Fatalf("restored child parent = %q, want %q", restoredChild.ParentID, rootBead.ID) + } + + deps := liveContractJSON[struct { + Children []beads.Bead `json:"children"` + }](t, baseURL, validator, http.MethodGet, cityBase+"/bead/"+url.PathEscape(rootBead.ID)+"/deps", nil, http.StatusOK) + if !beadListContains(deps.Children, childBead.ID) { + t.Fatalf("deps children = %#v, want child %s", deps.Children, childBead.ID) + } + graph := liveContractJSON[struct { + Beads []beads.Bead `json:"beads"` + Deps []contractGraphDep `json:"deps"` + }](t, baseURL, validator, http.MethodGet, cityBase+"/beads/graph/"+url.PathEscape(rootBead.ID), nil, http.StatusOK) + if !beadListContains(graph.Beads, rootBead.ID) { + t.Fatalf("graph beads = %#v, want root %s", graph.Beads, rootBead.ID) + } + if !beadListContains(graph.Beads, childBead.ID) { + t.Fatalf("graph beads = %#v, want child %s", graph.Beads, childBead.ID) + } + if !beadListContains(graph.Beads, siblingBead.ID) { + t.Fatalf("graph beads = %#v, want sibling %s", graph.Beads, siblingBead.ID) + } + for _, childID := range []string{childBead.ID, siblingBead.ID} { + if !liveContractGraphHasEdge(graph.Deps, rootBead.ID, childID, "parent-child") { + t.Fatalf("graph deps = %#v, want parent-child edge %s -> %s", graph.Deps, rootBead.ID, childID) + } + } + list := liveContractJSON[struct { + Items []beads.Bead `json:"items"` + Total int `json:"total"` + }](t, baseURL, validator, http.MethodGet, cityBase+"/beads?label=mc-live-contract&limit=50&rig="+url.QueryEscape(rigName), nil, http.StatusOK) + if list.Total < 3 || !beadListContains(list.Items, rootBead.ID) || !beadListContains(list.Items, siblingBead.ID) { + t.Fatalf("filtered beads = %+v, want root and sibling", list) + } + if listedSibling, ok := findLiveContractBead(list.Items, siblingBead.ID); ok && listedSibling.ParentID != rootBead.ID { + t.Fatalf("filtered sibling parent = %q, want %q", listedSibling.ParentID, rootBead.ID) + } + + waitForLiveContractEvent(t, baseURL, validator, cityBase+"/events", cityName, "city.ready", 10*time.Second) + liveContractJSON[contractEventList](t, baseURL, validator, http.MethodGet, "/v0/events?limit=50", nil, http.StatusOK) + runLiveContractReadSweep(t, baseURL, validator, specBytes, cityName, rigName) + + for _, id := range []string{siblingBead.ID, childBead.ID, rootBead.ID} { + liveContractJSON[struct { + Status string `json:"status"` + }](t, baseURL, validator, http.MethodDelete, cityBase+"/bead/"+url.PathEscape(id), nil, http.StatusOK) + } + liveContractJSON[struct { + Status string `json:"status"` + }](t, baseURL, validator, http.MethodDelete, cityBase+"/rig/"+url.PathEscape(rigName), nil, http.StatusOK) + + unregister := liveContractJSON[struct { + OK bool `json:"ok"` + Name string `json:"name"` + Path string `json:"path"` + }](t, baseURL, validator, http.MethodPost, cityBase+"/unregister", nil, http.StatusAccepted) + if !unregister.OK || unregister.Name != cityName { + t.Fatalf("unregister response = %+v, want ok=true name=%q", unregister, cityName) + } + waitForCityAbsent(t, baseURL, validator, cityName, 45*time.Second) +} + +type contractEventList struct { + Items []contractEvent `json:"items"` + Total int `json:"total"` +} + +type contractEvent struct { + Type string `json:"type"` + Subject string `json:"subject"` + City string `json:"city"` + Payload struct { + Name string `json:"name"` + Path string `json:"path"` + Error string `json:"error"` + } `json:"payload"` +} + +type liveContractReadProbe struct { + pathTemplate string + path string + skipReason string +} + +type contractRigList struct { + Items []contractRig `json:"items"` + Total int `json:"total"` +} + +type contractRig struct { + Name string `json:"name"` + Path string `json:"path"` +} + +type contractGraphDep struct { + From string `json:"from"` + To string `json:"to"` + Kind string `json:"kind"` +} + +func liveContractValidator(t *testing.T, specBytes []byte) openapivalidator.Validator { + t.Helper() + doc, err := libopenapi.NewDocument(specBytes) + if err != nil { + t.Fatalf("build OpenAPI document: %v", err) + } + v, errs := openapivalidator.NewValidator(doc) + if len(errs) > 0 { + t.Fatalf("construct OpenAPI validator: %v", errs) + } + return v +} + +func liveContractJSON[T any](t *testing.T, baseURL string, v openapivalidator.Validator, method, path string, body any, wantStatus int) T { + t.Helper() + raw := liveContractRequest(t, baseURL, v, method, path, body, wantStatus) + var out T + if err := json.Unmarshal(raw, &out); err != nil { + t.Fatalf("%s %s decode response: %v\nbody: %s", method, path, err, string(raw)) + } + return out +} + +func liveContractRequest(t *testing.T, baseURL string, v openapivalidator.Validator, method, path string, body any, wantStatus int) []byte { + t.Helper() + req, err := liveContractHTTPRequest(baseURL, method, path, body) + if err != nil { + t.Fatalf("%s %s build request: %v", method, path, err) + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("%s %s: %v", method, path, err) + } + defer resp.Body.Close() //nolint:errcheck + raw, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatalf("%s %s read response: %v", method, path, err) + } + if resp.StatusCode != wantStatus { + t.Fatalf("%s %s status = %d, want %d; body: %s", method, path, resp.StatusCode, wantStatus, string(raw)) + } + if v != nil { + validateLiveContractResponse(t, v, req, resp, raw) + } + return raw +} + +func liveContractHTTPRequest(baseURL, method, path string, body any) (*http.Request, error) { + var reader io.Reader + if body != nil { + raw, err := json.Marshal(body) + if err != nil { + return nil, err + } + reader = bytes.NewReader(raw) + } + req, err := http.NewRequest(method, baseURL+path, reader) + if err != nil { + return nil, err + } + req.Header.Set("Accept", "application/json") + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + if method != http.MethodGet { + req.Header.Set("X-GC-Request", "live-contract") + } + return req, nil +} + +func assertLiveContractStreamOpens(t *testing.T, baseURL, path string) { + t.Helper() + ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second) + defer cancel() + req, err := http.NewRequestWithContext(ctx, http.MethodGet, baseURL+path, nil) + if err != nil { + t.Fatalf("build stream request %s: %v", path, err) + } + req.Header.Set("Accept", "text/event-stream") + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("GET %s stream: %v", path, err) + } + defer resp.Body.Close() //nolint:errcheck + if resp.StatusCode != http.StatusOK { + raw, _ := io.ReadAll(resp.Body) + t.Fatalf("GET %s stream status = %d, want 200; body: %s", path, resp.StatusCode, string(raw)) + } + contentType := resp.Header.Get("Content-Type") + if !strings.Contains(contentType, "text/event-stream") { + t.Fatalf("GET %s stream content-type = %q, want text/event-stream", path, contentType) + } +} + +func validateLiveContractResponse(t *testing.T, v openapivalidator.Validator, req *http.Request, resp *http.Response, raw []byte) { + t.Helper() + resp.Body = io.NopCloser(bytes.NewReader(raw)) + ok, valErrs := v.ValidateHttpResponse(req, resp) + _ = resp.Body.Close() + if ok { + return + } + var details strings.Builder + for _, ve := range valErrs { + fmt.Fprintf(&details, "%s - %s\n", ve.Message, ve.Reason) + for _, se := range ve.SchemaValidationErrors { + fmt.Fprintf(&details, " %s at %s\n", se.Reason, se.FieldPath) + } + } + t.Fatalf("%s %s response does not match OpenAPI schema:\n%sbody: %s", req.Method, req.URL.Path, details.String(), string(raw)) +} + +func waitForLiveContractEvent(t *testing.T, baseURL string, v openapivalidator.Validator, path, subject, eventType string, timeout time.Duration) { + t.Helper() + deadline := time.Now().Add(timeout) + var lastErr error + for time.Now().Before(deadline) { + events, err := liveContractEventList(baseURL, v, path) + if err != nil { + lastErr = err + time.Sleep(250 * time.Millisecond) + continue + } + for _, event := range events.Items { + if event.Subject == subject && event.Type == eventType { + return + } + if event.Subject == subject && strings.HasSuffix(event.Type, "_failed") { + t.Fatalf("event %s for %s failed: %+v", event.Type, subject, event.Payload) + } + } + time.Sleep(250 * time.Millisecond) + } + t.Fatalf("timed out waiting for %s for %s from %s; last error: %v", eventType, subject, path, lastErr) +} + +func liveContractEventList(baseURL string, v openapivalidator.Validator, path string) (contractEventList, error) { + req, err := liveContractHTTPRequest(baseURL, http.MethodGet, path, nil) + if err != nil { + return contractEventList{}, err + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return contractEventList{}, err + } + defer resp.Body.Close() //nolint:errcheck + raw, err := io.ReadAll(resp.Body) + if err != nil { + return contractEventList{}, err + } + if resp.StatusCode != http.StatusOK { + return contractEventList{}, fmt.Errorf("GET %s status %d: %s", path, resp.StatusCode, string(raw)) + } + if v != nil { + resp.Body = io.NopCloser(bytes.NewReader(raw)) + ok, valErrs := v.ValidateHttpResponse(req, resp) + _ = resp.Body.Close() + if !ok { + return contractEventList{}, fmt.Errorf("GET %s response does not match OpenAPI schema: %v; body: %s", path, valErrs, string(raw)) + } + } + var events contractEventList + if err := json.Unmarshal(raw, &events); err != nil { + return contractEventList{}, err + } + return events, nil +} + +func waitForCityAbsent(t *testing.T, baseURL string, v openapivalidator.Validator, cityName string, timeout time.Duration) { + t.Helper() + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + cities := liveContractJSON[struct { + Items []struct { + Name string `json:"name"` + } `json:"items"` + Total int `json:"total"` + }](t, baseURL, v, http.MethodGet, "/v0/cities", nil, http.StatusOK) + found := false + for _, city := range cities.Items { + if city.Name == cityName { + found = true + break + } + } + if !found { + return + } + time.Sleep(500 * time.Millisecond) + } + t.Fatalf("timed out waiting for city %q to disappear from /v0/cities", cityName) +} + +func waitForLiveContractRig(t *testing.T, baseURL string, v openapivalidator.Validator, cityBase, rigName, rigDir string, timeout time.Duration) { + t.Helper() + deadline := time.Now().Add(timeout) + var lastErr error + for time.Now().Before(deadline) { + rigs, err := liveContractRigList(baseURL, v, cityBase) + if err != nil { + lastErr = err + time.Sleep(250 * time.Millisecond) + continue + } + for _, rig := range rigs.Items { + if rig.Name == rigName && rig.Path == rigDir { + return + } + } + time.Sleep(250 * time.Millisecond) + } + t.Fatalf("timed out waiting for rig %q at %q; last error: %v", rigName, rigDir, lastErr) +} + +func liveContractRigList(baseURL string, v openapivalidator.Validator, cityBase string) (contractRigList, error) { + req, err := liveContractHTTPRequest(baseURL, http.MethodGet, cityBase+"/rigs", nil) + if err != nil { + return contractRigList{}, err + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return contractRigList{}, err + } + defer resp.Body.Close() //nolint:errcheck + raw, err := io.ReadAll(resp.Body) + if err != nil { + return contractRigList{}, err + } + if resp.StatusCode != http.StatusOK { + return contractRigList{}, fmt.Errorf("GET %s/rigs status %d: %s", cityBase, resp.StatusCode, string(raw)) + } + if v != nil { + resp.Body = io.NopCloser(bytes.NewReader(raw)) + ok, valErrs := v.ValidateHttpResponse(req, resp) + _ = resp.Body.Close() + if !ok { + return contractRigList{}, fmt.Errorf("GET %s/rigs response does not match OpenAPI schema: %v", cityBase, valErrs) + } + } + var rigs contractRigList + if err := json.Unmarshal(raw, &rigs); err != nil { + return rigs, err + } + return rigs, nil +} + +func runLiveContractReadSweep(t *testing.T, baseURL string, v openapivalidator.Validator, specBytes []byte, cityName, rigName string) { + t.Helper() + probes := collectLiveContractReadProbes(t, specBytes, cityName, rigName) + if len(probes) == 0 { + t.Fatal("OpenAPI read sweep found no GET probes") + } + for _, probe := range probes { + t.Run("GET "+probe.pathTemplate, func(t *testing.T) { + if probe.skipReason != "" { + t.Skip(probe.skipReason) + } + liveContractRequest(t, baseURL, v, http.MethodGet, probe.path, nil, http.StatusOK) + }) + } +} + +func collectLiveContractReadProbes(t *testing.T, specBytes []byte, cityName, rigName string) []liveContractReadProbe { + t.Helper() + var spec struct { + Paths map[string]map[string]json.RawMessage `json:"paths"` + } + if err := json.Unmarshal(specBytes, &spec); err != nil { + t.Fatalf("decode OpenAPI paths: %v", err) + } + + probes := make([]liveContractReadProbe, 0, len(spec.Paths)) + for pathTemplate, pathItem := range spec.Paths { + if _, ok := pathItem["get"]; !ok { + continue + } + if strings.Contains(pathTemplate, "/stream") || hasLiveContractUnboundPathParams(pathTemplate) { + continue + } + path := strings.ReplaceAll(pathTemplate, "{cityName}", url.PathEscape(cityName)) + path = appendLiveContractDefaultQuery(path, pathTemplate, rigName) + probes = append(probes, liveContractReadProbe{ + pathTemplate: pathTemplate, + path: path, + skipReason: liveContractProbeSkipReason(pathTemplate), + }) + } + return probes +} + +func hasLiveContractUnboundPathParams(pathTemplate string) bool { + for _, part := range strings.Split(pathTemplate, "/") { + if strings.HasPrefix(part, "{") && strings.HasSuffix(part, "}") && part != "{cityName}" { + return true + } + } + return false +} + +func appendLiveContractDefaultQuery(path, pathTemplate, rigName string) string { + query := url.Values{} + switch { + case strings.HasSuffix(pathTemplate, "/formulas") || strings.HasSuffix(pathTemplate, "/formulas/feed"): + query.Set("scope_kind", "rig") + query.Set("scope_ref", rigName) + case strings.HasSuffix(pathTemplate, "/orders/feed"): + query.Set("limit", "25") + query.Set("scope_kind", "rig") + query.Set("scope_ref", rigName) + case strings.HasSuffix(pathTemplate, "/readiness") || strings.HasSuffix(pathTemplate, "/provider-readiness"): + query.Set("fresh", "false") + case strings.HasSuffix(pathTemplate, "/beads"): + query.Set("limit", "50") + case strings.HasSuffix(pathTemplate, "/mail"): + query.Set("limit", "50") + } + if len(query) == 0 { + return path + } + return path + "?" + query.Encode() +} + +func liveContractProbeSkipReason(pathTemplate string) string { + switch { + case strings.HasSuffix(pathTemplate, "/extmsg/bindings"), + strings.HasSuffix(pathTemplate, "/extmsg/groups"), + strings.HasSuffix(pathTemplate, "/extmsg/transcript"): + return "requires a real session/conversation identity" + case strings.HasSuffix(pathTemplate, "/orders/history"): + return "requires a scoped order name fixture" + default: + return "" + } +} + +func assertLiveContractSpec(t *testing.T, specBytes []byte) { + t.Helper() + var spec struct { + Components struct { + Schemas map[string]struct { + OneOf []map[string]string `json:"oneOf"` + Properties map[string]any `json:"properties"` + } `json:"schemas"` + } `json:"components"` + } + if err := json.Unmarshal(specBytes, &spec); err != nil { + t.Fatalf("decode OpenAPI spec: %v", err) + } + for _, field := range []string{"metadata", "parent"} { + if _, ok := spec.Components.Schemas["BeadCreateInputBody"].Properties[field]; !ok { + t.Fatalf("BeadCreateInputBody missing %q property", field) + } + } + if _, ok := spec.Components.Schemas["BeadUpdateBody"].Properties["parent"]; !ok { + t.Fatal("BeadUpdateBody missing parent property") + } + + var cityPayloadRefs []string + for _, branch := range spec.Components.Schemas["EventPayload"].OneOf { + ref := branch["$ref"] + if strings.Contains(ref, "City") { + cityPayloadRefs = append(cityPayloadRefs, ref) + } + } + if len(cityPayloadRefs) != 1 || !strings.Contains(cityPayloadRefs[0], "CityLifecyclePayload") { + t.Fatalf("EventPayload city lifecycle branches = %#v, want exactly CityLifecyclePayload", cityPayloadRefs) + } +} + +func containsString(items []string, want string) bool { + for _, item := range items { + if item == want { + return true + } + } + return false +} + +func beadListContains(items []beads.Bead, id string) bool { + for _, item := range items { + if item.ID == id { + return true + } + } + return false +} + +func findLiveContractBead(items []beads.Bead, id string) (beads.Bead, bool) { + for _, item := range items { + if item.ID == id { + return item, true + } + } + return beads.Bead{}, false +} + +func liveContractGraphHasEdge(items []contractGraphDep, from, to, kind string) bool { + for _, item := range items { + if item.From == from && item.To == to && item.Kind == kind { + return true + } + } + return false +} diff --git a/test/integration/huma_binary_test.go b/test/integration/huma_binary_test.go index 010567e812..902087883d 100644 --- a/test/integration/huma_binary_test.go +++ b/test/integration/huma_binary_test.go @@ -31,12 +31,19 @@ import ( func TestHumaBinary_SupervisorBootsAndServesSpec(t *testing.T) { bin := buildGCBinary(t) - gcHome := t.TempDir() // macOS caps AF_UNIX paths at ~104 chars. t.TempDir() paths on - // macOS are long enough that /gc/supervisor.sock blows - // past the limit. Use a short /tmp-rooted dir for XDG_RUNTIME_DIR - // so the socket path stays under the limit. - runtimeDir := shortTempDir(t) + // macOS are long enough that /supervisor.sock blows past + // the limit. An isolated GC_HOME override keeps the supervisor + // socket under GC_HOME, so both GC_HOME and XDG_RUNTIME_DIR must + // live under the short /tmp-rooted test directory. + root := shortTempDir(t) + gcHome := filepath.Join(root, "home") + runtimeDir := filepath.Join(root, "run") + for _, dir := range []string{gcHome, runtimeDir} { + if err := os.MkdirAll(dir, 0o755); err != nil { + t.Fatalf("mkdir %s: %v", dir, err) + } + } port := reserveFreePort(t) writeSupervisorConfig(t, gcHome, port) if err := seedDoltIdentityForRoot(gcHome); err != nil { @@ -283,3 +290,402 @@ func waitHTTP(t *testing.T, url string, deadline time.Duration) { } } } + +// TestHumaBinary_CityCreateAsync exercises the async POST /v0/city +// contract end-to-end against a live supervisor: subscribe to +// /v0/events/stream, POST /v0/city, verify the handler returns 202 +// immediately with {ok, name, path}, then assert a city.ready event +// for that city name arrives on the SSE stream. This is the test MC's +// live contract harness implicitly needs — without it, any +// regression in Scaffold, the reconciler's city.ready emission, or +// the supervisor event multiplexer would ship unnoticed. +// +// Build-tagged `integration`; run with: +// +// go test -tags=integration ./test/integration/ -run TestHumaBinary_CityCreateAsync +func TestHumaBinary_CityCreateAsync(t *testing.T) { + bin := buildGCBinary(t) + + root := shortTempDir(t) + gcHome := filepath.Join(root, "home") + runtimeDir := filepath.Join(root, "run") + for _, dir := range []string{gcHome, runtimeDir} { + if err := os.MkdirAll(dir, 0o755); err != nil { + t.Fatalf("mkdir %s: %v", dir, err) + } + } + port := reserveFreePort(t) + writeSupervisorConfig(t, gcHome, port) + if err := seedDoltIdentityForRoot(gcHome); err != nil { + t.Fatalf("seed dolt identity: %v", err) + } + + baseURL := "http://127.0.0.1:" + strconv.Itoa(port) + env := integrationEnvFor(gcHome, runtimeDir, true) + + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + cmd := exec.CommandContext(ctx, bin, "supervisor", "run") + cmd.Env = env + stderr, err := cmd.StderrPipe() + if err != nil { + t.Fatalf("stderr pipe: %v", err) + } + if err := cmd.Start(); err != nil { + t.Fatalf("start supervisor: %v", err) + } + var supervisorLog strings.Builder + go func() { _, _ = io.Copy(&supervisorLog, stderr) }() + t.Cleanup(func() { + cancel() + _ = cmd.Wait() + if t.Failed() { + t.Logf("supervisor stderr:\n%s", supervisorLog.String()) + } + }) + + waitHTTP(t, baseURL+"/health", 10*time.Second) + + // 1. POST /v0/city. Expected: 202 Accepted, body contains name + // matching the directory basename. We POST first because the + // supervisor event stream rejects subscriptions when no event + // providers are registered (503 no_providers), which is the + // case before any city exists. + cityDir := filepath.Join(gcHome, "async-test-city") + body := `{"dir":"` + cityDir + `","provider":"claude"}` + postReq, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+"/v0/city", strings.NewReader(body)) + if err != nil { + t.Fatalf("build post request: %v", err) + } + postReq.Header.Set("Content-Type", "application/json") + postReq.Header.Set("X-GC-Request", "true") + postStart := time.Now() + postResp, err := http.DefaultClient.Do(postReq) + if err != nil { + t.Fatalf("POST /v0/city: %v", err) + } + postDur := time.Since(postStart) + postBody, _ := io.ReadAll(postResp.Body) + _ = postResp.Body.Close() + if postResp.StatusCode != http.StatusAccepted { + t.Fatalf("POST /v0/city status = %d, want 202; body: %s", postResp.StatusCode, string(postBody)) + } + if postDur > 20*time.Second { + t.Errorf("POST /v0/city took %s, want fast scaffold response (<20s); async contract is broken", postDur) + } + var createResp struct { + OK bool `json:"ok"` + Name string `json:"name"` + Path string `json:"path"` + } + if err := json.Unmarshal(postBody, &createResp); err != nil { + t.Fatalf("decode create response: %v; body: %s", err, string(postBody)) + } + if !createResp.OK { + t.Errorf("ok = false; body: %s", string(postBody)) + } + if createResp.Name == "" { + t.Fatalf("empty city name in response; body: %s", string(postBody)) + } + if createResp.Path != cityDir { + t.Errorf("path = %q, want %q", createResp.Path, cityDir) + } + t.Logf("POST /v0/city returned 202 in %s for city %q", postDur.Round(time.Millisecond), createResp.Name) + + // 2. Subscribe to /v0/events/stream. No retry: Scaffold writes + // the city to cities.toml synchronously before POST returns, and + // TransientCityEventProviders reads cities.toml directly, so the + // mux contains this city's event provider by the time the client + // receives 202. after_cursor=0 requests replay from the start + // so the client doesn't miss city.ready if it fires between POST + // return and subscribe. + streamCtx, streamCancel := context.WithTimeout(context.Background(), 90*time.Second) + t.Cleanup(streamCancel) + streamReq, err := http.NewRequestWithContext(streamCtx, http.MethodGet, baseURL+"/v0/events/stream?after_cursor=0", nil) + if err != nil { + t.Fatalf("build stream request: %v", err) + } + streamReq.Header.Set("Accept", "text/event-stream") + streamResp, err := http.DefaultClient.Do(streamReq) + if err != nil { + t.Fatalf("GET /v0/events/stream: %v", err) + } + defer streamResp.Body.Close() //nolint:errcheck + if streamResp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(streamResp.Body) + t.Fatalf("GET /v0/events/stream status = %d, want 200; body: %s", streamResp.StatusCode, string(body)) + } + + // Collect events on a background goroutine; surface them via a + // channel so the test body can block until the expected one + // arrives (or a timeout fires). + eventLines := make(chan string, 128) + go readSSEFrames(streamResp.Body, eventLines) + + // 3. Wait for city.ready (or city.init_failed) on the SSE stream + // whose envelope Subject == createResp.Name. This is the async + // completion contract the MC live harness relies on. + deadline := time.After(120 * time.Second) + for { + select { + case <-deadline: + t.Fatalf("timed out waiting for city.ready for %q; collected %d lines so far", createResp.Name, len(eventLines)) + case line, ok := <-eventLines: + if !ok { + t.Fatalf("SSE stream closed before city.ready for %q arrived", createResp.Name) + } + // SSE "data:" lines carry JSON envelopes. Ignore + // heartbeats, comments, framing lines. + if !strings.HasPrefix(line, "data: ") { + continue + } + payload := strings.TrimPrefix(line, "data: ") + var env struct { + Type string `json:"type"` + Subject string `json:"subject"` + } + if err := json.Unmarshal([]byte(payload), &env); err != nil { + continue + } + if env.Subject != createResp.Name { + continue + } + switch env.Type { + case "city.ready": + t.Logf("received city.ready for %q — async contract satisfied", createResp.Name) + return + case "city.init_failed": + t.Fatalf("received city.init_failed for %q: %s", createResp.Name, payload) + } + } + } +} + +// TestHumaBinary_CityUnregisterAsync exercises the async +// POST /v0/city/{cityName}/unregister contract end-to-end against a +// live supervisor. Creates a city, waits for city.ready, then POSTs +// unregister and asserts city.unregistered arrives on the same SSE +// stream. Symmetric with TestHumaBinary_CityCreateAsync. +// +// Build-tagged `integration`; run with: +// +// go test -tags=integration ./test/integration/ -run TestHumaBinary_CityUnregisterAsync +func TestHumaBinary_CityUnregisterAsync(t *testing.T) { + bin := buildGCBinary(t) + + root := shortTempDir(t) + gcHome := filepath.Join(root, "home") + runtimeDir := filepath.Join(root, "run") + for _, dir := range []string{gcHome, runtimeDir} { + if err := os.MkdirAll(dir, 0o755); err != nil { + t.Fatalf("mkdir %s: %v", dir, err) + } + } + port := reserveFreePort(t) + writeSupervisorConfig(t, gcHome, port) + if err := seedDoltIdentityForRoot(gcHome); err != nil { + t.Fatalf("seed dolt identity: %v", err) + } + + baseURL := "http://127.0.0.1:" + strconv.Itoa(port) + env := integrationEnvFor(gcHome, runtimeDir, true) + + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + cmd := exec.CommandContext(ctx, bin, "supervisor", "run") + cmd.Env = env + stderr, err := cmd.StderrPipe() + if err != nil { + t.Fatalf("stderr pipe: %v", err) + } + if err := cmd.Start(); err != nil { + t.Fatalf("start supervisor: %v", err) + } + var supervisorLog strings.Builder + go func() { _, _ = io.Copy(&supervisorLog, stderr) }() + t.Cleanup(func() { + cancel() + _ = cmd.Wait() + if t.Failed() { + t.Logf("supervisor stderr:\n%s", supervisorLog.String()) + } + }) + + waitHTTP(t, baseURL+"/health", 10*time.Second) + + // 1. Create a city. + cityDir := filepath.Join(gcHome, "unregister-test-city") + body := `{"dir":"` + cityDir + `","provider":"claude"}` + postReq, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+"/v0/city", strings.NewReader(body)) + if err != nil { + t.Fatalf("build post request: %v", err) + } + postReq.Header.Set("Content-Type", "application/json") + postReq.Header.Set("X-GC-Request", "true") + postResp, err := http.DefaultClient.Do(postReq) + if err != nil { + t.Fatalf("POST /v0/city: %v", err) + } + postBody, _ := io.ReadAll(postResp.Body) + _ = postResp.Body.Close() + if postResp.StatusCode != http.StatusAccepted { + t.Fatalf("POST /v0/city status = %d, want 202; body: %s", postResp.StatusCode, string(postBody)) + } + var createResp struct { + Name string `json:"name"` + Path string `json:"path"` + } + if err := json.Unmarshal(postBody, &createResp); err != nil { + t.Fatalf("decode create response: %v; body: %s", err, string(postBody)) + } + + // 2. Subscribe to /v0/events/stream and wait for city.ready so + // we know the reconciler has fully adopted the city (the + // unregister reconcile path we're testing operates on the + // running set). + streamCtx, streamCancel := context.WithTimeout(context.Background(), 180*time.Second) + t.Cleanup(streamCancel) + streamReq, err := http.NewRequestWithContext(streamCtx, http.MethodGet, baseURL+"/v0/events/stream?after_cursor=0", nil) + if err != nil { + t.Fatalf("build stream request: %v", err) + } + streamReq.Header.Set("Accept", "text/event-stream") + streamResp, err := http.DefaultClient.Do(streamReq) + if err != nil { + t.Fatalf("GET /v0/events/stream: %v", err) + } + defer streamResp.Body.Close() //nolint:errcheck + if streamResp.StatusCode != http.StatusOK { + b, _ := io.ReadAll(streamResp.Body) + t.Fatalf("GET /v0/events/stream status = %d, want 200; body: %s", streamResp.StatusCode, string(b)) + } + + eventLines := make(chan string, 256) + go readSSEFrames(streamResp.Body, eventLines) + + readyDeadline := time.After(120 * time.Second) +ready: + for { + select { + case <-readyDeadline: + t.Fatalf("timed out waiting for city.ready for %q", createResp.Name) + case line, ok := <-eventLines: + if !ok { + t.Fatalf("SSE stream closed before city.ready for %q arrived", createResp.Name) + } + if !strings.HasPrefix(line, "data: ") { + continue + } + var env struct { + Type string `json:"type"` + Subject string `json:"subject"` + } + if err := json.Unmarshal([]byte(strings.TrimPrefix(line, "data: ")), &env); err != nil { + continue + } + if env.Subject == createResp.Name && env.Type == "city.ready" { + break ready + } + } + } + t.Logf("city %q ready; issuing unregister", createResp.Name) + + // 3. POST /v0/city/{cityName}/unregister. Expect 202. + unregURL := baseURL + "/v0/city/" + createResp.Name + "/unregister" + unregReq, err := http.NewRequestWithContext(ctx, http.MethodPost, unregURL, nil) + if err != nil { + t.Fatalf("build unregister request: %v", err) + } + unregReq.Header.Set("X-GC-Request", "true") + unregStart := time.Now() + unregResp, err := http.DefaultClient.Do(unregReq) + if err != nil { + t.Fatalf("POST unregister: %v", err) + } + unregDur := time.Since(unregStart) + unregBody, _ := io.ReadAll(unregResp.Body) + _ = unregResp.Body.Close() + if unregResp.StatusCode != http.StatusAccepted { + t.Fatalf("POST unregister status = %d, want 202; body: %s", unregResp.StatusCode, string(unregBody)) + } + if unregDur > 20*time.Second { + t.Errorf("POST unregister took %s, want fast response (<20s)", unregDur) + } + var unregBodyDecoded struct { + OK bool `json:"ok"` + Name string `json:"name"` + Path string `json:"path"` + } + if err := json.Unmarshal(unregBody, &unregBodyDecoded); err != nil { + t.Fatalf("decode unregister response: %v; body: %s", err, string(unregBody)) + } + // macOS resolves /tmp -> /private/tmp at some boundaries; strip + // either prefix so the test survives wherever the canonicalization + // kicked in. + canonicalize := func(p string) string { return strings.TrimPrefix(p, "/private") } + if !unregBodyDecoded.OK || unregBodyDecoded.Name != createResp.Name || canonicalize(unregBodyDecoded.Path) != canonicalize(createResp.Path) { + t.Errorf("unregister response mismatch: got %+v, want ok=true name=%q path=%q", unregBodyDecoded, createResp.Name, createResp.Path) + } + t.Logf("POST unregister returned 202 in %s", unregDur.Round(time.Millisecond)) + + // 4. Wait for city.unregistered (or city.unregister_failed) on + // the SSE stream. + unregDeadline := time.After(120 * time.Second) + for { + select { + case <-unregDeadline: + t.Fatalf("timed out waiting for city.unregistered for %q", createResp.Name) + case line, ok := <-eventLines: + if !ok { + t.Fatalf("SSE stream closed before city.unregistered for %q arrived", createResp.Name) + } + if !strings.HasPrefix(line, "data: ") { + continue + } + var env struct { + Type string `json:"type"` + Subject string `json:"subject"` + } + if err := json.Unmarshal([]byte(strings.TrimPrefix(line, "data: ")), &env); err != nil { + continue + } + if env.Subject != createResp.Name { + continue + } + switch env.Type { + case "city.unregistered": + t.Logf("received city.unregistered for %q — async unregister contract satisfied", createResp.Name) + return + case "city.unregister_failed": + t.Fatalf("received city.unregister_failed for %q: %s", createResp.Name, strings.TrimPrefix(line, "data: ")) + } + } + } +} + +// readSSEFrames scans a text/event-stream body line-by-line and ships +// each line to out. Returns when the underlying reader closes (EOF or +// connection drop). The channel is closed to signal "no more frames". +func readSSEFrames(body io.ReadCloser, out chan<- string) { + defer close(out) + buf := make([]byte, 0, 4096) + chunk := make([]byte, 4096) + for { + n, err := body.Read(chunk) + if n > 0 { + buf = append(buf, chunk[:n]...) + for { + i := strings.IndexByte(string(buf), '\n') + if i < 0 { + break + } + line := strings.TrimRight(string(buf[:i]), "\r") + buf = buf[i+1:] + out <- line + } + } + if err != nil { + return + } + } +}