diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..d63d464 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @showxu diff --git a/.github/RELEASE.md b/.github/RELEASE.md deleted file mode 100644 index fe5a075..0000000 --- a/.github/RELEASE.md +++ /dev/null @@ -1,59 +0,0 @@ -# Release publication and catalog notification - -Publish only an accepted plugin package bound to its reviewed source/tag and exact archive digest. -Keep an existing public tag and archive immutable. A candidate, draft or notification receipt does -not establish authenticated vendor or installed-host acceptance. - -The upload-release workflow promotes an accepted existing candidate into a matching draft; it does not make that draft public. After the accepted release becomes public, -`notify-catalog.yml` requests a complete catalog reconciliation. It also observes public edits, -channel promotion, unpublishing and deletion; those events never authorize catalog withdrawal by -themselves. The central publisher retains verified history and applies its reviewed withdrawal -policy. It verifies actual GitHub release sources rather than trusting an event payload. - -## Notification authority - -The workflow pins the website's central notification action to a reviewed full commit. Publish that -central commit before enabling a plugin workflow that references it. Review and update this pin -when adopting changes to the notification contract. The caller checks its immutable repository ID, -does not check out package code, and grants its own job token no repository permissions. - -Supply `CATALOG_DISPATCH_TOKEN` using existing reviewed authority with Actions write access to -`computer-mcp/computer-mcp.github.io` only. Website Contents write access is unnecessary. The action -can also receive an existing temporary token directly from a publishing job. Neither workflow -creates or persists credentials. Missing or rejected authority fails visibly; the -publisher's independent schedule still reconciles missed notifications. - -## Publication and retry - -A manual public release emits the release event. Publication performed with a repository's -`GITHUB_TOKEN` does not trigger ordinary release-event workflows. After that publication succeeds, -its automation must explicitly call this reusable workflow as a dependent job: - -```yaml -notify-catalog: - needs: publish - uses: ./.github/workflows/notify-catalog.yml - secrets: - CATALOG_DISPATCH_TOKEN: ${{ secrets.CATALOG_DISPATCH_TOKEN }} -``` - -Here `publish` is the job that actually makes the accepted release public, not the candidate-build -or draft-upload job. When using an existing short-lived token within that publishing job, invoke -the same pinned central action directly after publication instead. Keep token values out of command -arguments, printed output and release metadata. - -For an operator-driven publication or a missed/failed notification, explicitly dispatch: - -```sh -gh workflow run notify-catalog.yml --repo computer-mcp/plugin-codex --ref master -``` - -This schedules notification using its configured authority; it does not publish or rewrite a -release. Inspect the notification run and its returned central `run_url`. A successful dispatch -proves request acceptance only. Verify the central run completed successfully and the public index -contains the exact expected release identities and generation. If the release is already public and -notification fails, retry notification without changing or republishing the release. Complete -reconciliation is idempotent and repairs duplicate/missed events. - -See the central [catalog publication and notification contract](https://github.com/computer-mcp/computer-mcp.github.io/blob/master/docs/plugin-catalog.md) -for provenance, credentials, retry bounds and deployment semantics. diff --git a/.github/workflows/validate.yml b/.github/workflows/ci.yml similarity index 99% rename from .github/workflows/validate.yml rename to .github/workflows/ci.yml index aa1f2e3..87bc0a0 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Validate and package +name: CI on: pull_request: push: @@ -21,7 +21,7 @@ on: permissions: contents: read concurrency: - group: validate-${{ github.ref }}-${{ inputs.windows_validation_scope || 'all' }} + group: ci-${{ github.ref }}-${{ inputs.windows_validation_scope || 'all' }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: package: diff --git a/.github/workflows/upload-release.yml b/.github/workflows/release.yml similarity index 97% rename from .github/workflows/upload-release.yml rename to .github/workflows/release.yml index 99cf897..9d80a69 100644 --- a/.github/workflows/upload-release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,9 @@ -name: Upload verified release archives +name: Release on: workflow_dispatch: inputs: source_run: - description: Successful Validate and package run ID + description: Successful CI run ID required: true release_tag: description: Existing draft release tag bound to the verified commit @@ -35,7 +35,7 @@ jobs: jq -e --arg repo "$GH_REPO" ' .status == "completed" and .conclusion == "success" and .event == "push" and .head_branch == "master" and - .path == ".github/workflows/validate.yml" and + .path == ".github/workflows/ci.yml" and .head_repository.full_name == $repo' run.json > /dev/null source_sha=$(jq -r .head_sha run.json) [[ "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] diff --git a/AGENTS.md b/AGENTS.md index 9dda162..8caf73f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -68,11 +68,11 @@ Use this check: ## Task Route +- Before changing versions, dependencies, packaging or release workflows, read + `Documentation/Architecture/VersioningAndRelease.md` and use its existing project check entry points. + - For repository-native documentation placement, read `Documentation/README.md` before editing. -- For versions, dependencies and release work, read - `Documentation/Architecture/VersioningAndRelease.md`; use `Scripts/version.py` - and the existing package/dependency checks rather than another version ledger. - For current canonical structure, read `Documentation/Architecture/README.md` and the relevant architecture files. - For design-in-progress, use `Documentation/Proposals/*` when that subtree is diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1b75efb --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,40 @@ +# Changelog + +## 0.3.0 — 2026-09-29 + +- Projects the stable Codex App Server methods from swift-codex 0.4.1 with + typed parameter schemas; experimental methods keep their metadata. +- Thread reads default to metadata; history uses bounded turn and item pages + with native cursors. +- Runtime ownership covers active invocations, detached native work and writer + handoff; cancellation and release report confirmed and uncertain cleanup + separately. +- Adds scoped host services and native Windows x86_64 packages. Windows needs + Microsoft's Visual C++ v14 x64 runtime. macOS arm64 remains supported. + +## 0.2.1 — 2026-09-22 + +- Cancelling an in-flight App Server request retires its owned generation, so + the next request starts a fresh one. +- Shutdown uses an independent grace-period timer, so cleanup waits stay + bounded. + +## 0.2.0 — 2026-09-21 + +- Preserves native Codex configuration, sandbox and Full Access choices, + approvals and experimental protocol fields across App Server and Exec. +- Binds persisted state, thread ownership and managed worktree leases to the + verified caller. +- Uses swift-codex 0.2.2 and the Codex 0.154.0 App Server schema baseline. + +## 0.1.1 — 2026-09-13 + +- Release archives keep the plugin manifest byte for byte, which fixes + installation from GitHub. +- Packaging checks the built adapter against the architectures the manifest + declares. + +## 0.1.0 — 2026-09-13 + +- First release: App Server, Exec and Codex MCP capabilities through swift-codex + as standard MCP, for macOS arm64. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b215a10..0238f75 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,11 +6,30 @@ external dependencies and credentials. ## Validation -Run `swift build`, `swift test`, and strict `swift-format` lint. Exercise help, valid input and invalid input from outside the checkout. Keep command output contracts and generated resources covered by tests. +CI runs these checks on macOS; run them before opening a pull request. They need +Node.js, `jq` and `ripgrep`: + +```sh +python3 Scripts/version.py check +swift package resolve && git diff --exit-code -- Package.resolved +Scripts/verify-swift-codex-release-gate.sh +node --test Tests/schema-import.test.mjs +node Scripts/import-schema.mjs --check +swift format lint --strict --recursive Package.swift Sources Tests +swift build --build-tests --disable-automatic-resolution +swift test --skip-build --disable-automatic-resolution --no-parallel +python3 -m unittest discover -s Tests -p 'test_*.py' +python3 Scripts/package.py --output OUTPUT_DIRECTORY --configuration release +``` + +CI also packages and checks the Windows x86_64 archive on a Windows runner and +runs the organization brand check. Exercise help, valid input and invalid input +from outside the checkout. Keep command output contracts and generated resources +covered by tests. ## Documentation The [documentation index](Documentation/README.md) links current architecture and reference material. Agent work routes live in AGENTS.md; public usage lives in the root README. GitHub collaboration files belong in .github/ and contributor -policy belongs in root governance files. Execution notes belong in .agent/. +policy belongs in root governance files. diff --git a/Documentation/Architecture/Documentation.md b/Documentation/Architecture/Documentation.md index 0405499..640d275 100644 --- a/Documentation/Architecture/Documentation.md +++ b/Documentation/Architecture/Documentation.md @@ -6,8 +6,10 @@ CONTRIBUTING.md describes contributor checks. Current package facts belong in Documentation/Architecture/Package.md; commands, formats and operating detail belong in Documentation/Reference/. -Execution state and local validation evidence belong in .agent/. Durable -transitions or accepted decisions may have separate history records when needed. +Version and release rules belong in +Documentation/Architecture/VersioningAndRelease.md, and publication steps in +Documentation/Reference/Release.md. Durable transitions or accepted decisions +may have separate history records when needed. GitHub-specific configuration belongs in .github/. The SwiftPM products are executables. Their user-facing interface is the CLI/MCP contract, documented in the manual and reference pages. There is no public library API or DocC catalog. Any future public library API needs a catalog beside its owning target; generated .doccarchive output remains a build artifact. diff --git a/Documentation/Architecture/Package.md b/Documentation/Architecture/Package.md index ed9ef84..42ab448 100644 --- a/Documentation/Architecture/Package.md +++ b/Documentation/Architecture/Package.md @@ -14,7 +14,7 @@ dependency. | --- | --- | | `computer-mcp/swift-sdk` transport fork | Standard northbound MCP transport, tools and results; native Windows stdio and complete POSIX frame writes | | swift-codex | App Server client and Exec client, each with its own lifecycle | -| swift-subprocess 0.4.0 | Existing process infrastructure dependency | +| swift-subprocess 0.4.0 | Child process launch and line I/O on Apple platforms | | swift-argument-parser 1.8.2 | Named options, schema-comparison subcommand, validation and generated CLI help | | Apple Swift System 1.8.1 | Typed CRT descriptors for inherited Windows MCP pipe handles; already shared by the MCP dependency | | Apple Swift Crypto 4.5.2 | SHA-256 on Windows; Apple platforms retain CryptoKit | @@ -25,9 +25,9 @@ SDK version, resolved revision and public Git tag agree. CI requires this check before packaging. Its dependency notices ship with the adapter artifact, independently of host dependencies. -Argument Parser keeps command structure and help in one declaration instead of -fixed-position argument handling. Serving and schema comparison delegate to the -same use cases as before parameter parsing; parser errors do not start Codex. +Argument Parser keeps command structure and help in one declaration. Parsing +completes before serving or schema comparison starts, so parser errors never +start Codex. Use the SDK's public clients, including App Server raw request, notification and server-request access, for protocol initialization and request correlation. Domain ownership, input validation and MCP projection belong in this package. diff --git a/Documentation/Architecture/VersioningAndRelease.md b/Documentation/Architecture/VersioningAndRelease.md index d4d2df5..88e1161 100644 --- a/Documentation/Architecture/VersioningAndRelease.md +++ b/Documentation/Architecture/VersioningAndRelease.md @@ -5,7 +5,7 @@ The root `computer-mcp-plugin.toml` owns the adapter's version. rejects drift. `update --kind … --reason …` changes the manifest and refreshes the constant. `check --base COMMIT` rejects version regression; `check --tag vVERSION` verifies both the manifest version and the tag's commit. -CI checks the pull request base or previous main commit without changing files. +CI checks the pull request base or previous `master` commit without changing files. An actual packaged executable's `--version` must match the byte-identical packaged manifest. Never infer a candidate version from an installed adapter or another worktree. @@ -25,7 +25,7 @@ Codex executable version; these versions need not be equal. `Scripts/package.py --output …` checks the declared/generated versions and lock, builds the archive, validates its executable and emits a file/digest receipt. -The `Validate and package` workflow runs the same source checks and publishes an +The CI workflow (`ci.yml`) runs the same source checks and publishes an immutable CI candidate artifact. Check the relocated candidate with a standard MCP client and `Scripts/check-workflow.py --adapter … --codex …`. The latter uses an isolated home and fixed loopback model for native approvals, turns, @@ -34,7 +34,7 @@ model authentication. Host integration uses the Computer MCP repository's fixed installed-gateway checks against the exact adapter bytes. Accept the complete host/plugin/SDK combination before delivery. Create a formal -signed tag only for the accepted commit; `upload-release.yml` promotes the +signed tag only for the accepted commit; `release.yml` promotes the already-built artifact from its verified source run into the matching draft. The upload verifies every platform archive's inventory and manifest against the formal tag before uploading any asset. It promotes both declared native archives @@ -42,8 +42,10 @@ and their receipts from the same successful source run without rebuilding. An already uploaded asset must have the same digest; conflicting bytes fail. Candidate retries keep the intended product version and use a new run identity. A public tag and archive remain immutable. Only changed components are released. -See [Installation](../Reference/Installation.md) for packaging, installation, -upgrade and rollback commands. +`CHANGELOG.md` records each public release. See +[Installation](../Reference/Installation.md) for packaging, installation, +upgrade and rollback commands, and [Release](../Reference/Release.md) for +publication and catalog notification. Reusable successful evidence must match source, dependency lock, check definition, toolchain, target configuration and artifact digest. Missing or changed inputs diff --git a/Documentation/README.md b/Documentation/README.md index 6328142..d8191da 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -1,5 +1,7 @@ # Documentation +- [Architecture](Architecture/README.md): architecture overview and + documentation roles. - [Package architecture](Architecture/Package.md): targets, dependencies, connection ownership, process teardown, and executable support boundaries. - [Versioning and release](Architecture/VersioningAndRelease.md): version @@ -9,5 +11,9 @@ - [Host integration](Reference/HostIntegration.md): scoped callbacks, host authorization, managed registrations and failure recovery. - [Installation](Reference/Installation.md): relocatable artifacts, host settings, update/rollback/removal, and distribution boundaries. +- [State migration](Reference/StateMigration.md): offline transfer of + adapter-owned records from a Computer MCP snapshot. +- [Release](Reference/Release.md): publishing a release and notifying the + plugin catalog. The [root README](../README.md) is the public setup and command manual. diff --git a/Documentation/Reference/Installation.md b/Documentation/Reference/Installation.md index 3ace6cb..4e26f3a 100644 --- a/Documentation/Reference/Installation.md +++ b/Documentation/Reference/Installation.md @@ -57,7 +57,7 @@ published atomically without replacing any existing destination, including an empty directory created while the build is running. Failure removes only the packager's temporary staging directory; existing outputs remain unchanged. -The repository's `Validate and package` workflow runs on pull requests, pushes +The repository's CI workflow (`ci.yml`) runs on pull requests, `master` pushes and manual dispatch. It checks formatting, tests and the dependency lock, then retains both native ZIPs and receipts as downloadable workflow artifacts. A separate Windows job installs no Swift toolchain, relocates the exact ZIP and diff --git a/Documentation/Reference/Release.md b/Documentation/Reference/Release.md new file mode 100644 index 0000000..4cadd33 --- /dev/null +++ b/Documentation/Reference/Release.md @@ -0,0 +1,80 @@ +# Release + +This guide covers publishing an accepted release and notifying the official +plugin catalog. [Versioning and Release](../Architecture/VersioningAndRelease.md) +owns the version, acceptance and immutability rules. + +## Publish + +1. Confirm that `version` in `computer-mcp-plugin.toml` has not been released + and that the reviewed `master` commit has a successful CI run. +2. Accept that run's `codex-plugin-macos-arm64-` and + `codex-plugin-windows-x86_64-` artifacts as described in + [Versioning and Release](../Architecture/VersioningAndRelease.md), including + the host checks against the exact archive bytes. +3. Create a signed annotated tag `vX.Y.Z` on the accepted commit and push it. +4. Create a draft GitHub Release for the tag whose target is the full commit SHA. +5. Promote the CI artifacts into the draft: + + ```sh + gh workflow run release.yml --repo computer-mcp/plugin-codex --ref master \ + -f source_run=RUN_ID -f release_tag=vX.Y.Z + ``` + + The workflow accepts only a successful `master` push run of `ci.yml` for the + tagged commit. It checks every archive the manifest declares against the tag + before uploading each archive and its `.receipt.json`. It never + rebuilds and never publishes the draft. +6. Review the draft's assets and notes, then publish it. + +Each receipt records the archive digest, file inventory and build architecture; +the Windows receipt also records the required Microsoft runtime versions. + +## Catalog notification + +`notify-catalog.yml` runs when a release is published, edited, released, +unpublished or deleted, and on manual dispatch. It asks the website to reconcile +the complete catalog. The website verifies the actual GitHub releases rather +than the event payload, keeps verified history, and applies its own withdrawal +policy; an event never withdraws a release by itself. + +The workflow authenticates as the receiver-scoped catalog GitHub App through +the `CATALOG_APP_CLIENT_ID` variable and the `CATALOG_APP_PRIVATE_KEY` secret. +The App needs Actions write access to `computer-mcp/computer-mcp.github.io` only. +The job checks this repository's immutable ID, does not check out package code, +and grants its own token no repository permissions. Missing or rejected +authority fails the run visibly. + +The website's notification action is pinned to a reviewed full commit. Publish +that website commit first, then update the pin here when adopting a change to +the notification contract. + +A release published with a repository `GITHUB_TOKEN` does not trigger +release-event workflows. Automation that publishes that way calls the workflow +as a dependent of the job that makes the release public: + +```yaml +notify-catalog: + needs: publish + uses: ./.github/workflows/notify-catalog.yml + secrets: + CATALOG_APP_PRIVATE_KEY: ${{ secrets.CATALOG_APP_PRIVATE_KEY }} +``` + +## Retry + +For an operator-driven publication or a missed or failed notification, dispatch: + +```sh +gh workflow run notify-catalog.yml --repo computer-mcp/plugin-codex --ref master +``` + +A successful dispatch only proves the request was accepted. Follow the run's +`run_url` output to the website run, confirm it succeeded, and confirm the public +index lists the expected release. Retrying never requires changing or +republishing the release: reconciliation is idempotent, and the website's +hourly schedule also repairs missed notifications. + +The website's +[catalog publication and notification contract](https://github.com/computer-mcp/computer-mcp.github.io/blob/master/docs/plugin-catalog.md) +covers provenance, credentials, retry bounds and deployment. diff --git a/Documentation/Reference/ReleaseNotes-0.2.1.md b/Documentation/Reference/ReleaseNotes-0.2.1.md deleted file mode 100644 index 7f18697..0000000 --- a/Documentation/Reference/ReleaseNotes-0.2.1.md +++ /dev/null @@ -1,18 +0,0 @@ -# Codex Plugin 0.2.1 - -This compatible patch fixes two managed-runtime lifecycle cases: - -- Cancelling an in-flight App Server request retires its owned generation before - returning cancellation. A subsequent request can start a fresh generation. -- Shutdown uses an independent grace-period timer, so process-launch and polling - overhead cannot accumulate into an unbounded cleanup wait. Early exits reap - the timer and supervisor promptly. - -The manifest is the plugin version authority. Generated runtime metadata, -packaging checks and the version policy reject drift. The SDK remains on the -existing public 0.2.2 dependency; no new SDK release is required for these fixes. - -Regression coverage includes cancellation ownership, parent death, long-grace -early exit, trailing oversized frames, reconnect, and isolated standard-MCP -native approval/turn/release flows. Final host compatibility is recorded against -the exact candidate archive during coordinated Computer MCP acceptance. diff --git a/README.md b/README.md index 6027134..09ca186 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ Part of the [Computer MCP](https://computer-mcp.github.io/) family. **Wherever you chat, your computer is there.** This independent Swift package projects Codex execution through standard MCP. -It reuses Computer MCP's existing execution implementations and swift-codex. +It builds on swift-codex's App Server and Exec clients and the Computer MCP fork +of the MCP Swift SDK. The host owns registration, caller grants, workspace authorization and audit. The plugin does not link Computer MCP Core or install the vendor Codex binary. diff --git a/Scripts/check-windows-artifact.ps1 b/Scripts/check-windows-artifact.ps1 index 624192c..c7fc3e8 100644 --- a/Scripts/check-windows-artifact.ps1 +++ b/Scripts/check-windows-artifact.ps1 @@ -15,7 +15,7 @@ New-Item -ItemType Directory -Path $evidence -Force | Out-Null $runJSON = gh api "repos/$env:GITHUB_REPOSITORY/actions/runs/$SourceRun" if ($LASTEXITCODE -ne 0) { throw 'Cannot inspect source run' } $run = $runJSON | ConvertFrom-Json -if ($run.status -ne 'completed' -or $run.path -ne '.github/workflows/validate.yml' -or +if ($run.status -ne 'completed' -or $run.path -ne '.github/workflows/ci.yml' -or $run.head_repository.full_name -ne $env:GITHUB_REPOSITORY -or $run.head_sha -cnotmatch '^[0-9a-f]{40}$') { throw 'Source must be a completed validation run from this repository' } diff --git a/Sources/CodexAdapter/CodexAppServerProvider.swift b/Sources/CodexAdapter/CodexAppServerProvider.swift index 8c9f704..4b8673c 100644 --- a/Sources/CodexAdapter/CodexAppServerProvider.swift +++ b/Sources/CodexAdapter/CodexAppServerProvider.swift @@ -1279,20 +1279,6 @@ struct CodexAppServerProvider: Sendable { return value } - private static func optionalBoundedInt( - _ key: String, - in object: [String: JSONValue], - range: ClosedRange - ) throws -> Int? { - guard let raw = object[key] else { return nil } - guard let value = raw.intValue, range.contains(value) else { - throw CodexToolError.invalidArguments( - "codex.argument_invalid: '\(key)' must be an integer between \(range.lowerBound) and \(range.upperBound)." - ) - } - return value - } - private static let emptySchema = objectSchema() private static let cursorSchema = objectSchema(properties: [ "after_cursor": integerSchema(minimum: 0),