From 636c2429b91525fd3dc57d5f84710d97474ad537 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 08:24:52 +0000 Subject: [PATCH 1/9] chore: delete the source-copy verifier and provenance records scripts/verify-source-copy.py checked byte identity with the original import and no longer passes; nothing runs it. Remove it with provenance/, the CONTRIBUTING audit-trail sentence and their name-guard exceptions. --- CONTRIBUTING.md | 4 +- docs/maintainers.md | 2 +- provenance/README.md | 56 - provenance/source.json | 1308 -------------------- provenance/verification.md | 67 - scripts/check-names.test.py | 6 +- scripts/core-distribution-manifest.test.py | 2 +- scripts/name-allowlist.json | 22 +- scripts/verify-source-copy.py | 50 - 9 files changed, 8 insertions(+), 1509 deletions(-) delete mode 100644 provenance/README.md delete mode 100644 provenance/source.json delete mode 100644 provenance/verification.md delete mode 100644 scripts/verify-source-copy.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f0cd6ead7..60ed792fa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,11 +33,11 @@ This guide owns how to work in the repository: documentation ownership, the repo ## Repository boundary -This repository is the standalone execution substrate copied from Parsar at the revision in `provenance/source.json`. It holds the API and its migrations, the Runtime protocol and daemon, Harness adapters, shared execution packages, the standalone Core Web console and build/test tools. +This repository is the standalone execution substrate, copied from the Parsar repository. It holds the API and its migrations, the Runtime protocol and daemon, Harness adapters, shared execution packages, the standalone Core Web console and build/test tools. Product users, workspaces, model catalogs, business assets, the Parsar product Web, product API and product migrations remain in Parsar. Do not import `server/`, `apps/parsar/`, product CLI/plugin packages or their deployment stack. -Preserve copied Runtime and protocol behavior. Go import paths use this repository's module and do not require fetching the original repository. The source snapshot and per-file hashes are an audit trail; future Core development need not preserve them. Do not automatically sync or delete the original repository's Core. +Preserve copied Runtime and protocol behavior. Go import paths use this repository's module and do not require fetching the original repository. Do not automatically sync or delete the original repository's Core. ### Product and execution service separation diff --git a/docs/maintainers.md b/docs/maintainers.md index fb1ce0561..b1bec0177 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -35,7 +35,7 @@ The build reuses the Core, Web, Runtime, SDK and helper builders. The manifest r The control archive carries no Runtime image or node execution artifacts; the offline archive carries them. The [download contract](../deploy/install/README.md#download-contract) describes how nodes obtain them. -A distribution carries the docs listed in `BUNDLED_DOCS` in `scripts/core-distribution-manifest.py`. Links between bundled docs stay relative; every other relative link is rewritten to the same file on GitHub at the bundle's commit. The build fails when a link or anchor does not resolve, and `make check-distribution` runs the same check on every tracked Markdown file outside `example/` and `provenance/`. Update the list when you add or move a doc that the installer or its output refers to. +A distribution carries the docs listed in `BUNDLED_DOCS` in `scripts/core-distribution-manifest.py`. Links between bundled docs stay relative; every other relative link is rewritten to the same file on GitHub at the bundle's commit. The build fails when a link or anchor does not resolve, and `make check-distribution` runs the same check on every tracked Markdown file outside `example/`. Update the list when you add or move a doc that the installer or its output refers to. ### Native installers diff --git a/provenance/README.md b/provenance/README.md deleted file mode 100644 index 8237b7980..000000000 --- a/provenance/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Source import - -This is a tracked-source snapshot from -[`MiniMax-AI-Dev/parsar@72ab4d37d49245f15b63d34f5741780e540bcec0`](https://github.com/MiniMax-AI-Dev/parsar/tree/72ab4d37d49245f15b63d34f5741780e540bcec0). -`source.json` records the original SHA-256 of every imported file. No untracked -files, credentials, build artifacts or source Git history were imported. -The source repository was not modified or stripped of Core. - -## Scope - -| Included | Purpose | -| --- | --- | -| `services/agents-api` | Service, workers, providers, operator commands, migrations, generated queries, tests and deployment files | -| `contracts/agents-api` | Pinned protocol, schema/types, extensions and coverage evidence | -| `apps/parsar-daemon` | Native execution daemon and existing adapters | -| `internal/agentdaemon`, `internal/agentskill`, `internal/runtimecrypto`, `internal/obs/log` | Shared execution protocol, placement, assets, crypto and logging | -| `packages/agents-client` | Core HTTP client and protocol tests, independent of product business code | -| `packages/claude-sdk-adapter`, `packages/mcode-harness`, `packages/tsconfig` | Existing alternative native execution adapters and their build dependencies | -| Selected `scripts` and workflows | API/runtime build, packaging, database/protocol and native checks | - -Parsar's Web, product server/database, business CLI, plugin UI, product deployment -and business documentation remain exclusively in the source repository. Existing -daemon compatibility helpers stay with the unchanged daemon; their presence does -not bring a product backend or make it an execution prerequisite. - -## Adaptations - -Execution sources, tests, migrations and protocol artifacts are byte-identical to -the source snapshot. Five imported files have packaging-only adaptations: - -- `go.mod`/`go.sum`: prune dependencies used only by the excluded product. -- `pnpm-lock.yaml`: prune excluded product workspace importers/dependencies using - pinned pnpm 10.30.3; preserve the native adapter versions. -- `services/agents-api/RELEASE.md` and `HOSTED-RELEASE.md`: link new release commit - IDs to this repository. - -Root README, contributor rules, Makefile, Node workspace and the complete-check -workflow are standalone scaffolding. The contributor guide retains source Core -architecture rules. Product-only checks are absent; equivalent Core persistence, -native/runtime and generated-query checks remain required. - -To audit the initial import: - -```sh -python3 scripts/verify-source-copy.py -``` - -This audit is specific to the import commit. It does not prohibit subsequent Core -changes. Upstream native sources remain pinned in their existing manifests and -must be fetched using the documented builders. This repository is not an offline -vendor archive and does not claim additional live model coverage. - -## Validation - -See [the import acceptance record](verification.md) for commands, environment, -results and limits. No existing mx deployment is changed by this import. diff --git a/provenance/source.json b/provenance/source.json deleted file mode 100644 index 728a6a8f5..000000000 --- a/provenance/source.json +++ /dev/null @@ -1,1308 +0,0 @@ -{ - "source_repository": "https://github.com/MiniMax-AI-Dev/parsar", - "source_commit": "72ab4d37d49245f15b63d34f5741780e540bcec0", - "copied_roots": [ - "services/agents-api/", - "contracts/agents-api/", - "apps/parsar-daemon/", - "internal/agentdaemon/", - "internal/agentskill/", - "internal/obs/log/", - "internal/runtimecrypto/", - "packages/agents-client/", - "packages/claude-sdk-adapter/", - "packages/codex-executor/", - "packages/codex-harness/", - "packages/mcode-harness/", - "packages/tsconfig/" - ], - "files": { - ".github/workflows/actionlint.yml": "cb81bc0e45661f7113d07250a57b9b1f67e733544b34948ef2543e722afb071c", - ".github/workflows/agents-api.yml": "87429b30fe4e9256f9b74ad5c9cfabfb07e73aabb5030904c497a87e4571cbc0", - ".github/workflows/agents-executor.yml": "4552ecbf1b0d2fc6a94d9f0c7b9134165b760f37b41484293f9093a4a4e14ca7", - ".github/workflows/agents-harness.yml": "4cd5fb9c51cd45cdf040aefaa5cce7513ea60d08910befd31a7f0b4feb5be398", - "LICENSE": "64ff4748b63ecf4b4475c64dbf3f22de3d348e9fd41c0d51d66c976710683087", - "apps/parsar-daemon/.gitignore": "aaccb1a00557171b31d00a99a6a2666856e417964732490685cdcba9f02de491", - "apps/parsar-daemon/cmd/parsar-daemon/main.go": "020ecb5a248d57d01181023eb8788b78ed11bcdb3ab72f747c39360aac11e87a", - "apps/parsar-daemon/internal/agent/binpath/binpath.go": "cd5872bf957d1cabebdf6759102d37e41be848dc81872f04f4c755560aa5e6b4", - "apps/parsar-daemon/internal/agent/binpath/binpath_test.go": "5868f81ecacf1167e1cde149ba514e1710670e11c67078f4d0ffba8ce6c44ae0", - "apps/parsar-daemon/internal/agent/claudecode/ask.go": "2c3ca61e615838872a3671a272c6785c7608a1a82214c0e76c1a095b0d3dbba6", - "apps/parsar-daemon/internal/agent/claudecode/ask_test.go": "91007631db4d6457b13875be1d69ff0dc71d1a078fd076f9866202d70024d615", - "apps/parsar-daemon/internal/agent/claudecode/export_test.go": "ff3c5f512833256eb4c4f806dadf4797544b3db74c8540e8353a4f43575ce272", - "apps/parsar-daemon/internal/agent/claudecode/install_concurrency_test.go": "d99d36f9400c5d4e76c563812fcc2cd734efbcc5797fd463b2a7b461a2795a71", - "apps/parsar-daemon/internal/agent/claudecode/options.go": "1520ae27b6efff1996c331484d1969b7bdb5a59e7dc8614c4dce0e4ad3d98f7a", - "apps/parsar-daemon/internal/agent/claudecode/options_test.go": "011b0be8aa5c8edf47533d11acf66b03c170e873ff3fd74a263de44534f222b5", - "apps/parsar-daemon/internal/agent/claudecode/parser.go": "09e46395b05e7f5505556455b8af0704944f53895c4f81a17489974e9adc23a4", - "apps/parsar-daemon/internal/agent/claudecode/parser_partial_block_test.go": "9ebfb404149a0d759e438330963fb00b08c9277690ec734e0ff2fcb4c5241f94", - "apps/parsar-daemon/internal/agent/claudecode/parser_result.go": "900890618ad1a4a11b0761f7456f9301ebf338d150e30a4f3f2e75bbff01062f", - "apps/parsar-daemon/internal/agent/claudecode/parser_result_test.go": "179eeeda9809c2a71c9dd8df5e5889164ba01a252ee59e6bfa3c4c77997fe8a4", - "apps/parsar-daemon/internal/agent/claudecode/parser_test.go": "eb924409e5e8eb0a3fe1e095f3b7c4e7d9e2e2af61868780d434b9a37fc8c7e9", - "apps/parsar-daemon/internal/agent/claudecode/permission.go": "3ffa0a52ddf361498593fc734f72c88a81eb4e1b2d04a573c8682e7c9649ea5d", - "apps/parsar-daemon/internal/agent/claudecode/permission_test.go": "8654eaf7ffe874a8c4473a80478d20ba6ef7da0be453c08874b6c048526f6033", - "apps/parsar-daemon/internal/agent/claudecode/plugins.go": "1f71e396b9d4d3138c2836849f175eba4a54b9ac4d3a30c3826edf425dc849f0", - "apps/parsar-daemon/internal/agent/claudecode/plugins_install.go": "64009a0b2e411fa02bb4287244017c2a031987f04089d80022bdcf0083798e9c", - "apps/parsar-daemon/internal/agent/claudecode/plugins_test.go": "63c03c4ca4d65a0073734d6644db43cebe7c23e3275b66e66caa3e700ac71173", - "apps/parsar-daemon/internal/agent/claudecode/session.go": "293e80bdf6fbce6a7bcd6f57ca55dcfbf5f9c096800f575186fe077cd64ffa6d", - "apps/parsar-daemon/internal/agent/claudecode/session_export_test.go": "be15bcc88d4e62d8d01f09f8e371ab30ec696f1d856d3a975ad88da8c09b550d", - "apps/parsar-daemon/internal/agent/claudecode/session_knowledge_test.go": "a5f1990279c52ad59b034f4e2008a9c92bba48db6f11959e5007fb614f72f29f", - "apps/parsar-daemon/internal/agent/claudecode/session_test.go": "cc7fc01cc078f1a75007414a91b2587eb0ecf826944d380e7f0ab3fc4df278ec", - "apps/parsar-daemon/internal/agent/claudecode/skills.go": "f5b57cf02a9ad57b5d0691ce2c69a921e76ed11cf20a1d5edd6358f15d487f9f", - "apps/parsar-daemon/internal/agent/claudecode/skills_test.go": "114e47ae0adfc22bbb71a4e3ae766d9b7d1847c1edc9e04a92ebe27828e7f746", - "apps/parsar-daemon/internal/agent/claudecode/version.go": "7692b917df4e1acaf975eebbeef5adcd843f9b5756ba906ac0d73cd85697f1fb", - "apps/parsar-daemon/internal/agent/claudecode/version_test.go": "da7b5bd1769e78335f56038c230e839188d16050144f18aeead2a69b35fc7937", - "apps/parsar-daemon/internal/agent/claudesdk/bridge_output.go": "3e9d251367bfd68c12076af2d3886befe29db3e7bb9e8d3092b0482051e3cfdf", - "apps/parsar-daemon/internal/agent/claudesdk/cancellation.go": "3acb22ba215c20f77ea910d4249628ce762bce7f9c3e563a6ed08c49131e2511", - "apps/parsar-daemon/internal/agent/claudesdk/cancellation_live_linux_test.go": "01f09d15f2e31bd6a08a43a49249a3780cc7b44547328857162a7ab36bbb05ba", - "apps/parsar-daemon/internal/agent/claudesdk/cancellation_test.go": "53c553e3e9a7e17d97b36f244933f67d4071eaa599e1d34a59e101f67a263c34", - "apps/parsar-daemon/internal/agent/claudesdk/commands.go": "a8f37734495c3a26cb85fdef0f546b16d3b56ed9f11316d107bddc1880b41d3d", - "apps/parsar-daemon/internal/agent/claudesdk/commands_session_test.go": "61a36afe71e9b4497fd2463cf08778500dc28d6386a7a416748dee0e0c9c8267", - "apps/parsar-daemon/internal/agent/claudesdk/commands_test.go": "020584edafbc39fece424ef7b8a33b86d773a8e44357c9227bf5954c8801cfb0", - "apps/parsar-daemon/internal/agent/claudesdk/execution_controls_test.go": "7a6c6877e8ac522d65ffe2404662e6da0f317460501d67e06b4b71fa5dd898b8", - "apps/parsar-daemon/internal/agent/claudesdk/functions.go": "57a0b4a5c01ce00f7649d54c370e4f7de2c661d796cfab606654896eb07a1a67", - "apps/parsar-daemon/internal/agent/claudesdk/functions_test.go": "ed92b02e411a1be46725577e5e28cfc3b2df79fa3d721a4b4d8e549358ed1e3e", - "apps/parsar-daemon/internal/agent/claudesdk/live_linux_test.go": "055d9ac5d34e950171cd9d9cebd1faa68ab412c0c3fe0805bc74a4fd83b279ae", - "apps/parsar-daemon/internal/agent/claudesdk/local.go": "0c0c53e03de618a6415c42776199135867940a45d7cd24eebef0c18b7af11549", - "apps/parsar-daemon/internal/agent/claudesdk/local_test.go": "b259e04b9f831c56be3b12d4fa2cf03fcc8b5e826cb15bc92dbfad8421f50534", - "apps/parsar-daemon/internal/agent/claudesdk/mcp.go": "53d5fdd149cb11f1419f71d0ef8c12a08a561a5fca22d9499a308c348d79bf3d", - "apps/parsar-daemon/internal/agent/claudesdk/mcp_bearer_test.go": "1814fc5a16a0d69d31a1076bdd0a109914c4b48a0defa87dce81366e050d8171", - "apps/parsar-daemon/internal/agent/claudesdk/mcp_test.go": "4b065854aaa440d3106ab1374dbb019ea5f35b0fe22c88bbb65b9fc02a53154a", - "apps/parsar-daemon/internal/agent/claudesdk/messages_test.go": "7cc9fb3007135965486abe45bb066ea04f99e8f30eb25c8e2f7ac9f903ef1656", - "apps/parsar-daemon/internal/agent/claudesdk/options.go": "9d138bd8f918ea26c52c7bb63e2b6149ac5fd537a3c76ceac74ca7af0cc4bbe5", - "apps/parsar-daemon/internal/agent/claudesdk/options_test.go": "54f7028a977bfb95595c525089c180ec5ae7461a3435ecea2e61408a70c8e3b1", - "apps/parsar-daemon/internal/agent/claudesdk/preparation.go": "4e4a4dc0488dafe44613a36c4d5f6ba00502d3c80bab725702987c73e4f96ae8", - "apps/parsar-daemon/internal/agent/claudesdk/preparation_fixture_test.go": "c6820a2f13346f1802a555be8373d25db5370e604b4e046bf79164b111190b78", - "apps/parsar-daemon/internal/agent/claudesdk/preparation_test.go": "9e0d3e5a2f2ca363235d4c3f4c96f81b275614ac83c5fa5e4643d03122a069d4", - "apps/parsar-daemon/internal/agent/claudesdk/provider.go": "9f330bacc1701eae343d2f2753a22039bdb445d036b44375d2517965766f261c", - "apps/parsar-daemon/internal/agent/claudesdk/readiness.go": "d1959e6e92846a7d049194cd070450c19fee487b3d5500742197655b6080cb8a", - "apps/parsar-daemon/internal/agent/claudesdk/readiness_test.go": "83f9466af5e0b030d22cdcb80286461e7e795c1b4f550e8e70b3a7317b540724", - "apps/parsar-daemon/internal/agent/claudesdk/restrictions_test.go": "ec2487b91d479ef58a8710d4671810e20ecc9ddeab3760499f5a98d55d3365f4", - "apps/parsar-daemon/internal/agent/claudesdk/session.go": "ba3af76744d609d8b550646f579012ab99b3cde81bf3f52097f2f4faf80acb40", - "apps/parsar-daemon/internal/agent/claudesdk/session_test.go": "550b98b9e5deccb36e2d2b107a864071ded4b6f759fa88be865e4a45b68061a0", - "apps/parsar-daemon/internal/agent/claudesdk/steering.go": "fed7627776ad8607ae739324fdbbb57763b81a943beb50d94760075824cf6b27", - "apps/parsar-daemon/internal/agent/claudesdk/steering_test.go": "0f44c63065f2baac11f236c9c7d7b36c981b3e1611b62bf7dd4fd6572a50ebef", - "apps/parsar-daemon/internal/agent/claudesdk/usage.go": "4fc8e6fa8f45e30b94170c34936f03b53f3fb049e4b03564708af7f0c63ef618", - "apps/parsar-daemon/internal/agent/claudesdk/usage_test.go": "7ec69563a2efdd554d182235a32a965ddd25487d6552309c1604f94c167d58a4", - "apps/parsar-daemon/internal/agent/claudesdk/workspace.go": "3ea6207ac7091a9cef0e6185d930764b9734b761ff96a5768be6712176e29772", - "apps/parsar-daemon/internal/agent/claudesdk/workspace_commands_live_linux_test.go": "be074dea166d6ae3ba3b610407abd412599a7be59b9e28b80499a05ede150847", - "apps/parsar-daemon/internal/agent/claudesdk/workspace_directory.go": "fed3481a554b5f7b13832ad010ead6d21695bbfc50c79e46e8eff08263d29743", - "apps/parsar-daemon/internal/agent/claudesdk/workspace_directory_live_linux_test.go": "974d50c5c1490caf5b92b1a131a908974e8e09216429976bc733fa8cf95c0ed5", - "apps/parsar-daemon/internal/agent/claudesdk/workspace_directory_test.go": "3e8eb6afddee99b7758835e234e6c0b60230672b984bda573022551430ef18d6", - "apps/parsar-daemon/internal/agent/claudesdk/workspace_launch_test.go": "bfbf67d7210fae477b6828fde12361e7a01a9862b48ce9f2766790386ba1fc74", - "apps/parsar-daemon/internal/agent/claudesdk/workspace_live_linux_test.go": "998aac3a5e0116c22709d4744d3f16ec38d2c8f77779308951fa70c14c7c9ccb", - "apps/parsar-daemon/internal/agent/claudesdk/workspace_read.go": "d31ccfd1c7ff31ce98378d0d49ee131a48c9ddda83ed3bce8faeb8b912f493fc", - "apps/parsar-daemon/internal/agent/claudesdk/workspace_read_live_linux_test.go": "1e90012d85720917b7677101d9bb8ff81576b3894aa93ceaaeae55213eaa5ba6", - "apps/parsar-daemon/internal/agent/claudesdk/workspace_read_test.go": "dd8c8ce37f43fc49478ad6f20140a78c2e3dc614adb3b192d7667adfa2ed1308", - "apps/parsar-daemon/internal/agent/claudesdk/workspace_test.go": "ad94817ad9bd57017b428f95074cc749651a72bf8c29e02bd7a75b36c00be0bb", - "apps/parsar-daemon/internal/agent/clirunner/process.go": "a935ec83cbc05193dd36ac4c7b970e81680bd427e350d322d44b6187e5d0d02e", - "apps/parsar-daemon/internal/agent/clirunner/process_group_linux_test.go": "9b5a73d2f7cc727c5af9efcd8982c07c16aa9289876ce81df2283579e1bd9f75", - "apps/parsar-daemon/internal/agent/clirunner/process_group_other.go": "c79dbbfd888e4f66eb60b56064226362ac435957a707ad85b0515d693b5ce5ed", - "apps/parsar-daemon/internal/agent/clirunner/process_group_unix.go": "67eb5ac1089e19124687252f8c2f0abd14a01d5ed73d6199e9c9d4dfc8a5794a", - "apps/parsar-daemon/internal/agent/clirunner/process_test.go": "2a91c6b6140b35d0880ddd1410befa4eea8f7ea09473a99e639b5126a528e89a", - "apps/parsar-daemon/internal/agent/codex/approval_policy.go": "289da803fd3f8813780e3053f4d83f58ea1e560cc9d84c10bc8f6cbc25674814", - "apps/parsar-daemon/internal/agent/codex/approval_policy_test.go": "2c9d1f8aca46da374319e26bbfd8959f84b1ae213512b71f7251b9bac5911208", - "apps/parsar-daemon/internal/agent/codex/cancellation_outcome.go": "5975366996aceb9e26df02e4901a8f51d6ad74a58a3d1d230590fffdb973a295", - "apps/parsar-daemon/internal/agent/codex/environment.go": "28963139a7706223d9eefe6be5fe43ab4df7d1fe46c463c22e757f794cf3b5d3", - "apps/parsar-daemon/internal/agent/codex/environment_local.go": "b56044ebaf5b1f25d0c8b432ed6daffda5357dc3aa372294a6b849a3f3289598", - "apps/parsar-daemon/internal/agent/codex/environment_remote.go": "1532fa3a2c4b2528afb20898ce3652a34e4f7f812042f040c17283549813e493", - "apps/parsar-daemon/internal/agent/codex/environment_remote_test.go": "08a5bff48a463d4c85236b3dc60df5fec605793c0d0684a03c101d0fc1bcde72", - "apps/parsar-daemon/internal/agent/codex/environment_remote_validation.go": "f96ec67b923debea96878fba583098b7c95dd5121fcc62cd16a017243f48cded", - "apps/parsar-daemon/internal/agent/codex/environment_remote_validation_test.go": "d454945e7f8a87b28291ab543f8257b2b201436ef512088997b3acc59d6af54d", - "apps/parsar-daemon/internal/agent/codex/environment_test.go": "0a1dde498f251f53f465abc8213446c424c3b4e93bed2b527684e5bcd50c768e", - "apps/parsar-daemon/internal/agent/codex/execution_controls.go": "07dd61dde3364dd7647b52d982492d05fdd45c25882318acb0e399fbc621e92e", - "apps/parsar-daemon/internal/agent/codex/execution_controls_test.go": "1ae18a124e963363a4e29c3cd457eee239ece5312ea29cbe76822ac2981653c1", - "apps/parsar-daemon/internal/agent/codex/export_test.go": "c2149376cc2eb2d4bc7a25796df2865017012c5883099dcdee86de4465a91ac9", - "apps/parsar-daemon/internal/agent/codex/functions.go": "2461ff35003f2d9a542bbe0b81833b0d2cadd094300bc88a9a26afa46fb5069e", - "apps/parsar-daemon/internal/agent/codex/functions_test.go": "972dcfa96088eb7ffb817ddb498f772fa68516bf3620f8d476a18b0cf3f61ed0", - "apps/parsar-daemon/internal/agent/codex/generation_config.go": "71e583bf57e00a1b9af2835cb8862aca604fdefae0b9d6c7a53e5cb878c26f6a", - "apps/parsar-daemon/internal/agent/codex/hosted_skills.go": "6001438ef40203aa41bfa0a302cb9d7086c8ded82a0ee52306b4e86796f81a2d", - "apps/parsar-daemon/internal/agent/codex/hosted_skills_test.go": "5157b03a4981be24ef428043e8441d22600a49f548a6b776ddac878b8d7665be", - "apps/parsar-daemon/internal/agent/codex/mcp_config.go": "5e414243c5c8a81b7b7ed34e508ca62f16cfd264d013f7667b2f4f8b117c7f42", - "apps/parsar-daemon/internal/agent/codex/mcp_config_test.go": "a4c292cd063f8c8dee36c0d8d04ac19e3bf062e555fdf9c6641d0136fbcfe20d", - "apps/parsar-daemon/internal/agent/codex/mcp_http.go": "0a532108d6561bc0dbb88f345af64a9cb07fcf59787ddab857a5d19d06d3ace9", - "apps/parsar-daemon/internal/agent/codex/mcp_http_bearer.go": "32c6976300a333cd82c73abd419ffef3d9f1386295e1fefb7da9aa5b884af62e", - "apps/parsar-daemon/internal/agent/codex/mcp_http_bearer_test.go": "a3a362b4943dcb80c7802e9d4e70412b257c38167bafcf315163922282c10b54", - "apps/parsar-daemon/internal/agent/codex/mcp_http_preflight.go": "d90385b8ca83506f723269a419cafe9aef79c0f6b7d0f8587a20b163142e44cc", - "apps/parsar-daemon/internal/agent/codex/mcp_http_preflight_test.go": "60a84121397f8d9a9f7a1abd1b8c09b145e0f7b71b61354009f15938f0c451d7", - "apps/parsar-daemon/internal/agent/codex/mcp_http_test.go": "4b8a82633ba5d66325f63ec06af0a8bcac10feec03e9fef780bd89cba06dbec2", - "apps/parsar-daemon/internal/agent/codex/mcp_required_test.go": "fa1791a91fa165169e0d143a911ce069d7526119dd7b3a251c9a2c51784fe069", - "apps/parsar-daemon/internal/agent/codex/model_catalog_command_other.go": "fec1405635dc0f338329d6275093b33680414b2536eca67ca2dd5c128db689b8", - "apps/parsar-daemon/internal/agent/codex/model_catalog_command_unix.go": "0fff6448d14faf33303dc59b44466b61c4a38784d2b5407064283de90775a6ce", - "apps/parsar-daemon/internal/agent/codex/model_catalog_command_unix_test.go": "3dcef5b4f99435b0a796db95fdffcfeaa730664082022acaa250d86d1fa5235c", - "apps/parsar-daemon/internal/agent/codex/model_verbosity.go": "a6c032dbcb326edfb53314c528bdd222adfbc4774065ee6f1d465f33c831d39a", - "apps/parsar-daemon/internal/agent/codex/model_verbosity_test.go": "331714b5e730667432803902df91f6d2763c13d56ca25481f0402ed7f4d83852", - "apps/parsar-daemon/internal/agent/codex/options.go": "7632cd7a482ee285b9efa076198f673bafe840ed56e393341cc92966b56b1388", - "apps/parsar-daemon/internal/agent/codex/options_test.go": "623dac85b868cb314e5fd3dba59483c2aaa8184e7339f6f41d0f4776317c95e6", - "apps/parsar-daemon/internal/agent/codex/permission_profile.go": "0e2257628701cc98c06cc9adec07e55d6e2748a42462398f9fbf4e6b4eae6dc2", - "apps/parsar-daemon/internal/agent/codex/permission_profile_test.go": "dd54ab4f56fee5821c57ddb5853465da5f6f84003304616522d84994ed5076ec", - "apps/parsar-daemon/internal/agent/codex/preparation.go": "c9cf20570aab31a2e8cf2980ba9003df690a9fbdf0d87d63e23cfbf14bb9c899", - "apps/parsar-daemon/internal/agent/codex/preparation_close_test.go": "1710efe5c1706812ba4887fe5b21fe3c385ad1e8fc5f5923670e4aa6fd372e65", - "apps/parsar-daemon/internal/agent/codex/preparation_helpers_test.go": "e471772785501b69db15d7c5fa2fdfd1651e26e1229e1cc9cf82ff1fe0d53344", - "apps/parsar-daemon/internal/agent/codex/preparation_router_test.go": "9acc27704cda15b85d57ec6fcee600d2b4c1c4e3009558a962af46fe1155c699", - "apps/parsar-daemon/internal/agent/codex/prepared.go": "4daeb8090f91117dbcef0490d717641d3ebb7d7bc359316596f24c3f8ec1a452", - "apps/parsar-daemon/internal/agent/codex/prepared_cancel_test.go": "750ad7cdf9830c0c1e2130608b08113a19a17ef217656bf532efbab7ca89b879", - "apps/parsar-daemon/internal/agent/codex/prepared_test.go": "e7096c650d3770da4052ca3d5f26f0b8c3b3456fabda83cc10cc8f0802beabba", - "apps/parsar-daemon/internal/agent/codex/private_harness.go": "8f7be96991effa7e509fcdeb479b88f3b61d852731ba7b6523d4596e1ffecfbe", - "apps/parsar-daemon/internal/agent/codex/private_harness_test.go": "1bb949e1a25e9fff04fd9001c1a76818d0aa4ce02a73cf050125209fbc35329d", - "apps/parsar-daemon/internal/agent/codex/protocol.go": "0f37b8b265cf4631a99c30d1d91b5abb2fde92ee226269a69d6ea8dea9f5e4ad", - "apps/parsar-daemon/internal/agent/codex/protocol_wire_test.go": "df0d79013f09242c24b27a4b2c18f78f27b12307e5be1e93a7a669a70b3eb4bf", - "apps/parsar-daemon/internal/agent/codex/provider_config.go": "72d291bcf59a89d0244b53d30bffd57e90af7701ac835f0535631b9301ff6396", - "apps/parsar-daemon/internal/agent/codex/provider_config_test.go": "9caff41f8949fd45b99668390e684247a4a2a730a8c22a1296197ae5a5147ff8", - "apps/parsar-daemon/internal/agent/codex/recovery.go": "22deb853a5fb1c636039fe7a0f2f4a63d520de6ac81aa9636ddf886b69b9cfe5", - "apps/parsar-daemon/internal/agent/codex/recovery_test.go": "d1fdf36793444caa0a82fd156bf3f0cc1d39ce7c6d37a391bacab65adbbfd333", - "apps/parsar-daemon/internal/agent/codex/resume.go": "495ad30e6e93aa58c7ee0bc1383aaba66a121aaa12f2686f5dce1b80e3cd4a99", - "apps/parsar-daemon/internal/agent/codex/resume_test.go": "aa70f7358a4cb3d02f440b86c5f831adb50bf03d228dc4600acbc97eb357b1e2", - "apps/parsar-daemon/internal/agent/codex/rpc.go": "0f8542848c3c0f247b4d90cb8f5baa716f48249b999704b0f8194519a39bed63", - "apps/parsar-daemon/internal/agent/codex/rpc_close.go": "3454a0e7d7533694828b48c87bb67e8e4ee9c2ee981c144f2e7631d69860de05", - "apps/parsar-daemon/internal/agent/codex/rpc_close_test.go": "6cc303e3f17b35d55be9989b44a44a6f373f7d95b20842d75991c23dce1ae9b5", - "apps/parsar-daemon/internal/agent/codex/rpc_process_test.go": "fad4b2cbba2e1d200e5ff0c946299e71ce358b2c2b32afe0f5a332b6c8b21658", - "apps/parsar-daemon/internal/agent/codex/rpc_request.go": "15835aae901915156a978bf69ca43d59b8d9acedc09bd1d717873c487faae74f", - "apps/parsar-daemon/internal/agent/codex/rpc_test.go": "876ec450d8d16e5485aefbd3dcb81115098d1c34b238632c6b4829bc9dcf99b0", - "apps/parsar-daemon/internal/agent/codex/rpc_write.go": "da8913f4318aedce0746057d3f3856bda0ec24977d957451f35cb7646c96f210", - "apps/parsar-daemon/internal/agent/codex/server_requests.go": "0101cb1d1dbf067d96d07b9cd5426b6d2763196175f21d02bcb237bdea746cc1", - "apps/parsar-daemon/internal/agent/codex/server_requests_mcp.go": "beaccc2b844d5f475267014f34c574f7964aeedfa2e192ed04d8628aa5912a18", - "apps/parsar-daemon/internal/agent/codex/server_requests_mcp_test.go": "52af6c4ea6534bd144983c5e8c1c7e6ef25365d4cca7d64002838c713f5de035", - "apps/parsar-daemon/internal/agent/codex/server_requests_test.go": "f25594ad791223a92a72ce096ed32432c7c368254ca377bd017923884fddd795", - "apps/parsar-daemon/internal/agent/codex/session.go": "4d2e7c6a10b2bc82121a65447b8aefb32e9caaaa8877a09be7f12d68f37ce989", - "apps/parsar-daemon/internal/agent/codex/session_cancel.go": "6ee16a2209a7826fb6220ff008ed8deb244296a80198901d2ba6506621531d2e", - "apps/parsar-daemon/internal/agent/codex/session_cancel_test.go": "274619a7da540800d953e83f6fb64134b5a46e80eb9bb0587df921ea80bb3825", - "apps/parsar-daemon/internal/agent/codex/session_cancel_write_test.go": "68e8aa9cec21b468c28674eccefabbc828357a18234a8853a5c8a396a1bec0df", - "apps/parsar-daemon/internal/agent/codex/session_command_output.go": "499f73869f3b6ebbb20e66cc2c546e2ceeea398e38349e4a2026af4a4a1d65fa", - "apps/parsar-daemon/internal/agent/codex/session_command_output_test.go": "f0f34e56a520bc5da59db34de239785dc5911efce2b79713c1f3702cc445f620", - "apps/parsar-daemon/internal/agent/codex/session_items.go": "d6652b08212a4822d809091c3384bd758a37c7739e4a2ef3c61e70371ae1d663", - "apps/parsar-daemon/internal/agent/codex/session_items_test.go": "0da9b2ee49524f39a8dc2bef7cbd2367d9a410ca70939f1c1a79e1ed1f9b2504", - "apps/parsar-daemon/internal/agent/codex/session_knowledge_test.go": "d43b973e73244fd77e1e97bc232c43f471e0a09dae998183b1cef1a99a49a4bf", - "apps/parsar-daemon/internal/agent/codex/session_log_test.go": "d81d8e0ddefca58d035e20ade5110f150de1905433c414859d18f4676bbe8646", - "apps/parsar-daemon/internal/agent/codex/session_messages.go": "3898536cce5dc521ae61fe705175e8fb5f64dbfd5ebd36140ab162dca40ec2cc", - "apps/parsar-daemon/internal/agent/codex/session_messages_test.go": "684bcfc92263e38bce12c7f1e1e3915d4c0201eca6923d0dfacc4432866fbea5", - "apps/parsar-daemon/internal/agent/codex/session_notifications.go": "56bd3189bcc17866470aeef303f6224eecf1a440f6a4882ca29078dae380ae5e", - "apps/parsar-daemon/internal/agent/codex/session_notifications_rpc_test.go": "b73ed10af256847266230a79cac101a68115935174a0383a72dae5f2972b9288", - "apps/parsar-daemon/internal/agent/codex/session_notifications_test.go": "92053e852ac0b1a4caed9c073b9d53fcc30c9e3c6ff3872568b75a1858445049", - "apps/parsar-daemon/internal/agent/codex/session_output.go": "5f61de02269f7822e68afb0c987863facf95689f77211ea5ad938fba48f4a969", - "apps/parsar-daemon/internal/agent/codex/session_plan.go": "2b6d47b250f39f8bc0ac6995f5fc21f9b07fcbab87919038fedd73861bc0adf9", - "apps/parsar-daemon/internal/agent/codex/session_policy_test.go": "f4f4e2b3a7505f88a11c93f1b4df69076aad0420f914941b26599fc0ef34c70b", - "apps/parsar-daemon/internal/agent/codex/session_run.go": "c6c89f9898a94ede294cc291b2faa548088b36f434947157eae77d592d4009ee", - "apps/parsar-daemon/internal/agent/codex/session_steering.go": "14c2eb149c768f161bee337f25610311748ea57ad1816c008bf8e645c387c326", - "apps/parsar-daemon/internal/agent/codex/session_steering_lifecycle_test.go": "4f0a1b4b11d65d4b5c0cd2877fb6796d52c06faa318e5378a5aba5bd1f0858dc", - "apps/parsar-daemon/internal/agent/codex/session_steering_receipt_test.go": "4b0bc4a1bb0bf2a5e12ec82e0b67a157951195e2e141325ebe660b000930ae16", - "apps/parsar-daemon/internal/agent/codex/session_steering_test.go": "55c09a2d12c701eea8536437fd05281e5e3cda333f9a422a5f427e6f5fae44dd", - "apps/parsar-daemon/internal/agent/codex/session_thread.go": "d9d1a2301325c38393b46c72d9ce6bdb8555ab08115971716bf64e4af3d19264", - "apps/parsar-daemon/internal/agent/codex/session_tools.go": "300b9a03ebfb9aed41bcb767bc4dbc32023853e26bcc9d5a1b8625cdaf003e4c", - "apps/parsar-daemon/internal/agent/codex/session_tools_test.go": "d3d169dfb9cbdd2b872fe0e8426535eb916ba36e0e886e141373399f8e4e6299", - "apps/parsar-daemon/internal/agent/codex/session_turn_error_test.go": "79cd94e4b46b443677cd92c1a2bede10935b4dc3c97da764df2653b0cc66b8c7", - "apps/parsar-daemon/internal/agent/codex/session_usage.go": "e7fac5cb68daa9348a3caa389bd713a281ece748461d48f1196734490e5f1357", - "apps/parsar-daemon/internal/agent/codex/session_usage_test.go": "9cb713b6e7cecbfd01cd70fd90ac7636a217ee925777c648ea04e856a2aa726d", - "apps/parsar-daemon/internal/agent/codex/skills.go": "aba4c03018148bb42018f4a758dcaaa3b86705eca22a7ce3c36fd6cf253a4ac4", - "apps/parsar-daemon/internal/agent/codex/skills_test.go": "323f25a09f2b81115d71c7c9018fcf5275878a21b64f35036a85cdc6c2f70a45", - "apps/parsar-daemon/internal/agent/codex/subagent_metadata.go": "8f4430679c41ae2f2f9cdd82227cde6aff20fdda158768a863cea8b082ac62ac", - "apps/parsar-daemon/internal/agent/codex/subagent_observations.go": "58460c963b4fcf02ca438b9968d95ed233b14f1de3b5dcfa51502a682cb0e5bc", - "apps/parsar-daemon/internal/agent/codex/subagent_observations_test.go": "f9e95eb69c537cb63ea045e211da11e9974b842a61bd8156bfb2d0dd8df25b9a", - "apps/parsar-daemon/internal/agent/codex/subagents.go": "c92d01f53c10462146e7525b720326d74ac956b3f9c445dfd4d6377cc7b662f8", - "apps/parsar-daemon/internal/agent/codex/subagents_test.go": "5607eba543af69833f753fa4357c5becc71469dff3c92d02fb3b8c8a4281bded", - "apps/parsar-daemon/internal/agent/codex/tool_environment.go": "37114ec973b2641672d1b5fbae78e9d9da8752e87ac7cd9fd348b50635ceb28b", - "apps/parsar-daemon/internal/agent/codex/tool_environment_test.go": "66490ee52148f9e63403fb6285a72d3957a355465f0ffc31215d72d852d68801", - "apps/parsar-daemon/internal/agent/codex/tool_observations.go": "6cda336ab1f02e7ea1a9a97b5eec63a0b9635bfa01ce2ca29b573615b344fa99", - "apps/parsar-daemon/internal/agent/codex/tool_observations_test.go": "69cd824c4fcea75538319d4218a6001d894eb854bf3c0c13f69f555ae63ecb23", - "apps/parsar-daemon/internal/agent/codex/verbosity_test.go": "bffc60525d56f5f100e6ce74edc666ec26e21e2a07f2da1a0bf453bd0c30de0f", - "apps/parsar-daemon/internal/agent/codex/version.go": "a49e4258611bdfafbef8b00f475704afe2dba4864a278ebf4ec6d79346307be8", - "apps/parsar-daemon/internal/agent/codex/web_search_test.go": "213552f74af899f549333d9e20c3489a87a1b08e33ad65565c5aa2fc716a8435", - "apps/parsar-daemon/internal/agent/codex/workspace_directory.go": "a667027c8da9e1f22780124ae1a55e39d2d96cfe83f308369b0ac59ef6068ad5", - "apps/parsar-daemon/internal/agent/codex/workspace_directory_test.go": "be2e22344257e54681823d8c35d283b6339a5bd2b08baf8ec92b0949400b9b8c", - "apps/parsar-daemon/internal/agent/codex/workspace_preparation.go": "c99e1727f5b5fc7d900370aa5f18995f03d4651cfb2400f5155e8fb3b17edaf3", - "apps/parsar-daemon/internal/agent/codex/workspace_preparation_failure_test.go": "58bc5ca19ea769a8386d0201f35ebe2d2256a5840f01c654aec7673277a69f8a", - "apps/parsar-daemon/internal/agent/codex/workspace_preparation_test.go": "2cc792bba6a7a6816626585081685be801d48d3758dc8b1afd32a2a351a5d161", - "apps/parsar-daemon/internal/agent/codex/workspace_read.go": "5df1077d8c43f0bcf863416dd383e29c671c777be153b5db3adb1537a0074705", - "apps/parsar-daemon/internal/agent/codex/workspace_read_test.go": "3789e254d9f90130ca4b688c20c302ef8a9d60f2db5f4e209ea2818d3ee2a3eb", - "apps/parsar-daemon/internal/agent/functions.go": "a0c68ea5511410ac3509e813084295878e4b8d4deee5a219d94b04084b7204cf", - "apps/parsar-daemon/internal/agent/installroot/lock.go": "a50ca3315f3dbc5a2035f49fd3b8858131798e05103b37fae5e08379b8f4a76b", - "apps/parsar-daemon/internal/agent/installroot/lock_test.go": "9d1596fc4cdc17ab92ea43330ec94f6c65be3762aa79b4c28f1d95016dd7d74d", - "apps/parsar-daemon/internal/agent/interactions.go": "5cda7ee84a16aed9d8cffccc761f0c71d6ca0ea3318d437be6c44d1fc6c1b183", - "apps/parsar-daemon/internal/agent/mcode/events.go": "ce16b92aa9e483af4b257cf37700bde866ddada1598d4be06c31028316385c5c", - "apps/parsar-daemon/internal/agent/mcode/execution.go": "6307301313d8671b80e01082701a7f4bcfb3797968e716f3f89cc4c6a5219809", - "apps/parsar-daemon/internal/agent/mcode/execution_test.go": "f36811cd2cd57108c41ad1aba34d21d9605a73678e4096c95e75f29663a7c0c8", - "apps/parsar-daemon/internal/agent/mcode/native_history_test.go": "09dcfc3717ad10449407535b97927784d0551c12cd1dbd9045f15555fc885a68", - "apps/parsar-daemon/internal/agent/mcode/native_test.go": "ca6c4e3a3e52d44e9cab97be6e44fbc03c091c6414770e1d543dde0cd57cb7c1", - "apps/parsar-daemon/internal/agent/mcode/options.go": "41de8639c0033d3aa72e7fd82a90ae08b690ae07e86c1795d75a7ba4e21d95ae", - "apps/parsar-daemon/internal/agent/mcode/options_test.go": "c3b9d62053fa6ba1bb8ff4b7e3ac694a10ca070894d2e5e61202cb77c991db54", - "apps/parsar-daemon/internal/agent/mcode/preparation.go": "0b134ba9f92ab8c6e7032714d35e39948262c4aed7c319972801534ab85a287b", - "apps/parsar-daemon/internal/agent/mcode/preparation_test.go": "8b46473410e96dc2a424c4b7552a3cf85b6076c4a069c5e3a68fe107c1536b6a", - "apps/parsar-daemon/internal/agent/mcode/protocol.go": "5b9f0c00f5d57e530da7646730f7bd08c518e125521a25c77a8f2a4e0d33d3ff", - "apps/parsar-daemon/internal/agent/mcode/questions.go": "2ccf7323067ee1f6ecb5e49128ee7f0761daa2f44fdf577c8cca88bfbff75eef", - "apps/parsar-daemon/internal/agent/mcode/questions_test.go": "b78ce41d2a9631bd43434fd8f389013cdf0278cd5f7b0eaba2e892dd429f6172", - "apps/parsar-daemon/internal/agent/mcode/session.go": "987ede3af8769aef742e1275ad05afa75fe5de3609f683792184e096b461f2ab", - "apps/parsar-daemon/internal/agent/mcode/session_test.go": "bf2065590d6734999cc54e4cdf7fb4632c93a766db9c98a2018b35899b708698", - "apps/parsar-daemon/internal/agent/mcode/steering.go": "a3d6602d069d40460148461b91a8b24b29643f57aec005c51362c1cee066298d", - "apps/parsar-daemon/internal/agent/mcode/steering_test.go": "34fea5dc67cef3510611dda755f3c2a6a766772f8285d343d7166aa9905f92f0", - "apps/parsar-daemon/internal/agent/mcode/tool_observations.go": "1c2ee6dd1f1d3d371e05823615862be1a440f1a4dd4ac8c03b6ec37ce842b7bb", - "apps/parsar-daemon/internal/agent/mcode/tool_observations_test.go": "cb125e34796dacb70009d34f306e71b598902c780001df4fbeda57257538b85e", - "apps/parsar-daemon/internal/agent/mcode/version.go": "5475bcf776ca8d259fd65d714ba41b0d74b0ffc047d52eb68d41a0f69daf2040", - "apps/parsar-daemon/internal/agent/mcode/version_test.go": "b2751d697abe1d40cd4c89446c50c1a06458aa5f810d6d7352b7f81a29ab7ac3", - "apps/parsar-daemon/internal/agent/mcode/workspace.go": "0e442e0d5b84a61be1a86a78875017810b60180ebaa9ab105e652d3f713b6d60", - "apps/parsar-daemon/internal/agent/mcode/workspace_readiness.go": "549fa9f136c294e488adc1f3841665d7722be735b4eb64199fd26cc26c81792b", - "apps/parsar-daemon/internal/agent/mcp.go": "c577fca19a945b88a4bf1a335751a71627c3065695b1e96f2dfb50945ff75b3d", - "apps/parsar-daemon/internal/agent/opencode/export_test.go": "f26572b0812fb43faa85ff7e2062e6942d9b9bcc6208776548214957832a02d1", - "apps/parsar-daemon/internal/agent/opencode/options.go": "a23eb04e75cc806d99bc57199b4865c0ea5a94e093d181025f22aa50409646c1", - "apps/parsar-daemon/internal/agent/opencode/options_test.go": "58c6e267b07ed7c12e1424d7d37b5d8723c6c17d399c793b5be710333137f6a7", - "apps/parsar-daemon/internal/agent/opencode/parser.go": "25b328e8500ef72b727841b6cef56ec69708bd3f63cb3652b61e251582d65ee1", - "apps/parsar-daemon/internal/agent/opencode/parser_test.go": "11c2ce3006b1d664c94e3615d80446be6c864cc04f279d545444163395b16241", - "apps/parsar-daemon/internal/agent/opencode/parser_tools_test.go": "09a4e0c43289fa6eacbc3736d65c548d3467cea8f4aad7ed8160442c0ed1ddf5", - "apps/parsar-daemon/internal/agent/opencode/session.go": "f5d3c7e72293cce39382f498ecf339f827cdd244a8ba5c76f248d51d690a672a", - "apps/parsar-daemon/internal/agent/opencode/session_model_test.go": "d117c14199efa442c0b75c9c1473b10ccbace1dab9e4c9d951d5c022095b7bc8", - "apps/parsar-daemon/internal/agent/opencode/session_test.go": "66a1008fa7c6fc0189562792fe81deb23384e55b3057f9c84761344e6c246d2b", - "apps/parsar-daemon/internal/agent/opencode/skills.go": "74a5f507e2804aaade2813cd4aeb8f1e6b16f4f203cdb5f0ab0990120c247c54", - "apps/parsar-daemon/internal/agent/opencode/skills_test.go": "26ca46932bb832ba99d5380ac17e30e9634bcbabd8d666cca065e0da49ab30de", - "apps/parsar-daemon/internal/agent/opencode/version.go": "d53be63877b5db8841dce5070222a5be184bd5c0581242ce787bedbaef8e4aa2", - "apps/parsar-daemon/internal/agent/opencode/version_test.go": "bf16235a75ef02b9cc587f51f055be6e2c6113639ce6810f5f5f1c47e450c572", - "apps/parsar-daemon/internal/agent/pi/export_test.go": "087ca5ddc9f9f6b47cf9d7384b855b90cbeb407fa6bfc15df2f3a2fa1aabbfff", - "apps/parsar-daemon/internal/agent/pi/options.go": "524ae616cbe01530b412c6fee981bc2117c59859a53eb7e55bed1b219dbc7f06", - "apps/parsar-daemon/internal/agent/pi/options_test.go": "ebceeb4c16951613508a7b6b292d67a19b3e42d66bd7b14a969284c04550a66e", - "apps/parsar-daemon/internal/agent/pi/parser.go": "7d178f1ca7013043eafbc20740bc18892461ab931a4aa264ec11e758cd5044d7", - "apps/parsar-daemon/internal/agent/pi/parser_test.go": "d6c5ea905366e1dc2df1b7e160757397bce5c5853ed3eb86a88e773eaab63d12", - "apps/parsar-daemon/internal/agent/pi/provider_config.go": "b2a394b371ad8799860336ddf552a7d67997f0695387eb1e092ab7ac65d7bfec", - "apps/parsar-daemon/internal/agent/pi/provider_config_test.go": "b53f96c66e8719f72fa8820a32cb264812b7e1b51674a30622dde28b98dee22f", - "apps/parsar-daemon/internal/agent/pi/session.go": "99b72b78918c941364b4e1fca2fbacfe40e669ca58003cd6ea2534eb89329bae", - "apps/parsar-daemon/internal/agent/pi/session_provider_test.go": "7f6a9cec600213f2e73c9531f4f96c132ef39583922697a2fba6f93507989259", - "apps/parsar-daemon/internal/agent/pi/session_skills_test.go": "f40787abf14eff9f50e9e2ab4741f7d61580791f9fc9ac38b487bf892efbca83", - "apps/parsar-daemon/internal/agent/pi/session_test.go": "e0e766c54d2aa9534659925a64a71ab93a59927b54a326f90cf8a1721da90814", - "apps/parsar-daemon/internal/agent/pi/skills.go": "b569ba874b49eb1ac6164e58471b516e9420e1f5032352e5db8cacf61478abf1", - "apps/parsar-daemon/internal/agent/pi/skills_concurrency_test.go": "a85125ce967caa63ee6236b60035e2376d530886b571a675a44a288fc4454051", - "apps/parsar-daemon/internal/agent/pi/skills_install.go": "f5a7604d933874419f74210a4bfdfb215961b7ee854c2a3b4316f5a2c1b5d5aa", - "apps/parsar-daemon/internal/agent/pi/skills_test.go": "6be2acf5630fa16c61237cc6f88f8a97d7bed4e4ac31ffa21e6f5bd012c16e35", - "apps/parsar-daemon/internal/agent/pi/version.go": "36b5a101837a69d44fa230533e839a9d7191ac29f94f2d1bf573b79cbd50194e", - "apps/parsar-daemon/internal/agent/pi/version_test.go": "b2551625babfea3567b3b96afd9c7254343647ed47baf1021cc8847e57d20b26", - "apps/parsar-daemon/internal/agent/preparation.go": "edb56c6320d02d6df9dbe91041fc39f1425298f5c59df7eb8d4529d193a8d2cd", - "apps/parsar-daemon/internal/agent/registry.go": "ebd7cca855a0ef65d441d5e3227171fa38ca2fc01b085a9594d502a06f6ba480", - "apps/parsar-daemon/internal/agent/registry_test.go": "ab7a7b43502a4c92bcd810a07f16860b0b5b9887ffc115dee4d81a46a846c94e", - "apps/parsar-daemon/internal/agent/runtime_paths.go": "819667d6a6cdbb10c52ce40112f5a38ff237bbdf7010a6b6901e24f1c76904ad", - "apps/parsar-daemon/internal/agent/runtime_paths_test.go": "0ef3cc1862beb3eeb110fc669a19785c81a1076b27fb259be8e3c6ce4628971f", - "apps/parsar-daemon/internal/agent/steering.go": "a5499b6853a9eda6f2873cb87dac5225f7875481d69c333a847db6c38e049478", - "apps/parsar-daemon/internal/agent/versionprobe/testutil/testutil.go": "6ca79604b8994848e16902da45a6b6239a49c8a4fd1f63f2eb08ec30e0a31335", - "apps/parsar-daemon/internal/agent/versionprobe/versionprobe.go": "4ce70cbd8c5aa1bf3fe4ca7cb6a74335a30c9430d54e8a75e09dc9db888b7d57", - "apps/parsar-daemon/internal/agent/workspace_directory.go": "ffc56d6f2a50653e911e79eefd711e77491ce826415037cc1ec3783d0e4aa2a1", - "apps/parsar-daemon/internal/agent/workspace_read.go": "60b8744b11035463d854122fcc50c333c7be1301cc5a311b4f250077796f28c3", - "apps/parsar-daemon/internal/agent/workspace_write.go": "a4f7b0b5bd685293272067a87fe8c7cc05b75c4a27bf4c27e0cf5839ec9d8825", - "apps/parsar-daemon/internal/auth/store.go": "9643d6f02bbe5551fcd77d8975f6acd0a95e70c174e1c7344c7b563db82844f6", - "apps/parsar-daemon/internal/auth/store_test.go": "be56cd3bbdf4bc2775b2c55c097ee1238f64be4be039bc9d7cd2aed6e4207bbc", - "apps/parsar-daemon/internal/authoring/bridge.go": "d068d1b68a416f44f27dfa1e5c142a80d2be32fd3d4e5791642d0a0d43faf35a", - "apps/parsar-daemon/internal/authoring/bridge_test.go": "1bdc2163444a33e5a605ef2250288b00f5caed5a901275393ec626a8b6d8c0bd", - "apps/parsar-daemon/internal/cli/agent_discovery.go": "d3b763cf3da80d18a4ea0c790c3f4595cd0267b91d35a226e209c50a9208b35c", - "apps/parsar-daemon/internal/cli/agent_registration.go": "dce1021a7cf310dfba0b27e15e84c51bef3aeb166f8e35194ae762cc4cc4ee9a", - "apps/parsar-daemon/internal/cli/authoring.go": "15b6ecdbf456fb062e819d0fa796ad87ed49761e165b859d2277515451bfd1e0", - "apps/parsar-daemon/internal/cli/authoring_test.go": "918c2d0962d968dfdbe4ebb711dbaf50561af12c40f21819050504591a3b88c7", - "apps/parsar-daemon/internal/cli/capability_downloads.go": "62b809e1b2ce49b010059df61740d78202813ff7e2f5d620b425f687975d9479", - "apps/parsar-daemon/internal/cli/capability_downloads_test.go": "93cfcdf35d74118383d2ac0b4ef0221a45e875b3df6d0dbbbfce2d724d786264", - "apps/parsar-daemon/internal/cli/claude_sdk.go": "bcb267c48d4086ddbb281b5777353c9311f008ce8d246862973bf91453a3ae72", - "apps/parsar-daemon/internal/cli/claude_sdk_live_linux_test.go": "961e607886641e5977217890220fe488f0f1189ec0f250b06679781e56bbff23", - "apps/parsar-daemon/internal/cli/claude_sdk_test.go": "871cb054fa33eb086ab27ac26ab2fb967e1eef9dcd1091b67e9b068133624bb4", - "apps/parsar-daemon/internal/cli/companion_path_test.go": "6caef90007ba98fb45e1b11ad8fee0fafb5c9414cc6a35b9a71eb193be37f121", - "apps/parsar-daemon/internal/cli/connect.go": "6a669333359b1fe8080f1b0eb3aff637801c6a97982171bbd2ba4d0bed01eabe", - "apps/parsar-daemon/internal/cli/connect_test.go": "063ae1e3a002bcd474c65bf4a434faef95e3f455c8391e17307d9523a4dcf4d8", - "apps/parsar-daemon/internal/cli/logout.go": "5918e7eb00c5b8c9e982d6019b47650c323c5dd46584f38c15dc3ec0d7c021cb", - "apps/parsar-daemon/internal/cli/logs.go": "de3b52a6f7fa5c71262f4ec5282fb4598fc3521cf886a8d7e055be9d97456333", - "apps/parsar-daemon/internal/cli/mcode.go": "384502574f35a5f0c9446fff5322c782dc684ba5f9ee64c3bef40008b5482671", - "apps/parsar-daemon/internal/cli/mcode_execution_test.go": "3b0f9f0be400464374ccdb23012c8567adfaecd9009d1917fb7e98dafde8c921", - "apps/parsar-daemon/internal/cli/mcode_workspace.go": "88524cd47e67f5805a912052b2e0187b0daca74591b485d5095c02a96745d32a", - "apps/parsar-daemon/internal/cli/mcp_test.go": "fa228ab581a2ed5d37e34d4263fce2810d5eb1880f09eeb897bd7ccb434a11a5", - "apps/parsar-daemon/internal/cli/pair.go": "bc3401bb9127b4dd2e8a1d3550074bf9e7dfbdd4ade591a4fa7dc4b2435bee1f", - "apps/parsar-daemon/internal/cli/pair_test.go": "42e3215d3bcfe941bfa2fb10146c87e65eb1912a84eea25cb2f17380138eb495", - "apps/parsar-daemon/internal/cli/placement.go": "ef4eb65af29adf30567305308b5f3b2f783f50eecca2228c634822e438731001", - "apps/parsar-daemon/internal/cli/preparation_test.go": "c387847457e8b87b8fb36e83c1698c74fc198aeacd1bc2f71fb5429d66665ae6", - "apps/parsar-daemon/internal/cli/root.go": "7660c18ef9478ed4e019465f1980224e949f41c1d464d2eb03fd53bdd7523387", - "apps/parsar-daemon/internal/cli/root_test.go": "51a4d00ccd3d74b2621bd428b0ace07d5f75ee5d14b64b2b28e8cc8867c5c196", - "apps/parsar-daemon/internal/cli/skill_upload.go": "405fb76e040e59c0e62c1b69bd11eaff9cc13a6950e4a99bc43254a7f30147b7", - "apps/parsar-daemon/internal/cli/skill_upload_test.go": "09cd28154033892ec6b2b4e4ff058d70c9c719c6247b935dd2442e1a2e4ae780", - "apps/parsar-daemon/internal/cli/status.go": "e7a23259d96da6410205644f9ae3219dfe473c5abd0590e51245991f5390e33b", - "apps/parsar-daemon/internal/cli/stop.go": "1056ee18dc0d1672087e43de0e0b4f15124e1b75c5594260f7b2834dad51327c", - "apps/parsar-daemon/internal/daemonize/fork.go": "1ee472a2b1564bda20c79b0fe6032877f0e58abe4b0a4686d2caa6b785bd72e3", - "apps/parsar-daemon/internal/daemonize/fork_test.go": "0cc79e1a73c3086c32792ee63284ae010abf3cce10c65d4131e4948112e7d448", - "apps/parsar-daemon/internal/daemonize/helpers_test.go": "cbcb6a754f0544c4594f980ea65e527178d558128d2f83a55ff353fe7c5d7d64", - "apps/parsar-daemon/internal/daemonize/logfile.go": "e00970c465929bc8c97a054ee05670ac3b00cee0f952bad20cab0071bb74be31", - "apps/parsar-daemon/internal/daemonize/logfile_test.go": "07485eb1a72c8c63912aed285a991d5bd25383d35c98e850e3fb8e7bec4bf262", - "apps/parsar-daemon/internal/daemonize/pidfile.go": "b5d64870e2b0de8606a931cd24b0fc3823cda74602de576c23c6c749fa46547b", - "apps/parsar-daemon/internal/daemonize/pidfile_test.go": "95c08e61c41f7e2ef870d7fe984a113a4ec569263736ecbd15d44eee33fc0da5", - "apps/parsar-daemon/internal/dispatch/cancellation.go": "0fa216f024cb2061704b3423bfd31435470375151dcd59fc744e72dd6791971f", - "apps/parsar-daemon/internal/dispatch/cancellation_test.go": "f6e222428c98d395aa6d7c4a42b29c2ece61c75a51497d7d6b7674caff4cf505", - "apps/parsar-daemon/internal/dispatch/capabilities.go": "c289b1fe04b64337c6431328d1f160814fd4cadb3422eaed73e1b8223eb9f013", - "apps/parsar-daemon/internal/dispatch/environment.go": "49e4de577b9a294053e5b4db9fb9513e92400dc619b01c81d1c3e6c49c9875a6", - "apps/parsar-daemon/internal/dispatch/environment_test.go": "f9a40dbedbb357e014bcc870521425b22522e748229175877c83d8a2d4b2fd70", - "apps/parsar-daemon/internal/dispatch/export_test.go": "b23124e6b3fb30273452dcb5831e17d35a3d536bc752f9bea77b0a349cd22282", - "apps/parsar-daemon/internal/dispatch/functions.go": "2d44a3bd3f9190bf131fd2dedd3bb77af8698403e28cee2c2f2cf84495ce2ab6", - "apps/parsar-daemon/internal/dispatch/functions_native_test.go": "eb47db4e935b79ffbbeca1e05535ad57affb5f759e7ebf4fc1e9359100e662ec", - "apps/parsar-daemon/internal/dispatch/functions_test.go": "1214950dedeaf15870f68809feddbfdc4c6e5c2b1a4b1b410076be3845f1cf56", - "apps/parsar-daemon/internal/dispatch/interaction_decisions.go": "8ca91d6a434d22d80c2087359e61d6119f3d5397012902064aa491d505319f7f", - "apps/parsar-daemon/internal/dispatch/local_directory.go": "cada3a0771cf63f991bd83ec1fb576fd0bb7fa73b7657bb5917ce1cd6d645c0a", - "apps/parsar-daemon/internal/dispatch/local_directory_test.go": "7bc10ec34dba29e22bb9abbe25e0f0b942952739cb3bda240c217d75a257e9af", - "apps/parsar-daemon/internal/dispatch/mcp_http.go": "ce99c09c523faba68f753a523ac58c5a931c80505f6547306a99bd35141db794", - "apps/parsar-daemon/internal/dispatch/mcp_http_test.go": "b921480225e6dc6de8f04ac6fbe9a72c96142291fc5d062b8a73af8d0754a309", - "apps/parsar-daemon/internal/dispatch/optional_interactions_test.go": "404299289d24ae5f5111789c7d0d8df9317b370282a1538d0d62e3c84880c60b", - "apps/parsar-daemon/internal/dispatch/output.go": "0e5ebece033c84c64ae96f9babd928597c00979d4bda580dde6cbd90768e829e", - "apps/parsar-daemon/internal/dispatch/preparation.go": "c7235ea02bacae393d87a9d34eb8231a8214008e1050f54501556382bbdaa1f5", - "apps/parsar-daemon/internal/dispatch/preparation_cancel.go": "95ac97bf96fcf34d48580901f7cd74a2ce2ad03541b65960f95ab9a389d321c8", - "apps/parsar-daemon/internal/dispatch/preparation_cancel_test.go": "5b1b04becad972ece908e209581245a641970ac916e2166213bdec9c3d5006ff", - "apps/parsar-daemon/internal/dispatch/preparation_cleanup.go": "534bbece5ee8c2bee58fd42c79fc3853abd0b0804035b567f994b648914857e6", - "apps/parsar-daemon/internal/dispatch/preparation_cleanup_test.go": "565a146a6f9a36dffde9d155174c80e9d2f83a3d78d78fa4c8e512f3a57f9e58", - "apps/parsar-daemon/internal/dispatch/preparation_start.go": "742ef410f9e934296f04c7882ea3b155375a74bb1b44a82d7978da4e68d5a318", - "apps/parsar-daemon/internal/dispatch/preparation_test.go": "455e29d1422a379a57eb503a73eaf3221ca96654897f897838e5976cfbfda839", - "apps/parsar-daemon/internal/dispatch/prepared_handoff.go": "f47e95760bfee7ff70284502891c0080076d03bc326320d17cf1e6d1c565fbae", - "apps/parsar-daemon/internal/dispatch/prepared_handoff_mutation_test.go": "24713f4277454b416149fe70c3d902d0d02037ea458b9ee574e0edac3627ad08", - "apps/parsar-daemon/internal/dispatch/prepared_handoff_test.go": "7efd0dd4081fcd319ee16d5f8f1f48f7092b920994fa5cfc64717dbbd21e3622", - "apps/parsar-daemon/internal/dispatch/prompt.go": "2710c95f6d26311cf205960323227c5c52d936c31e7cd1cd5896568915a1bd5f", - "apps/parsar-daemon/internal/dispatch/receipt_order_test.go": "27a53cbbd5b949cf554f3be4a3700afe70635cb5f66be2b4f28ca4aa6b248a49", - "apps/parsar-daemon/internal/dispatch/receipt_shutdown_test.go": "648daa69aee9148a8efe29bc48994a4eac58669891431b53723a65eccc441805", - "apps/parsar-daemon/internal/dispatch/router.go": "58681b04fe43b63466669f80162331b91b89617183e5d8eba2d75f3ecf7e34a8", - "apps/parsar-daemon/internal/dispatch/router_test.go": "b857d2aee21f1c16cc071929fe8edf401db6b3998bf145b39ed466b28b90c95d", - "apps/parsar-daemon/internal/dispatch/shutdown.go": "87385f283e950a21ba0b21643e3d88d6d48dc764b4f53bc591154a404c900985", - "apps/parsar-daemon/internal/dispatch/steering.go": "d335ecc603d028cb6401c5599632c86c1b3a269550451b95df9343eddc7fe111", - "apps/parsar-daemon/internal/dispatch/steering_lifetime.go": "453ef65cbb897d7430332f5ee4bf6032e5f751a8fa77d1d35cf455f73196fd4b", - "apps/parsar-daemon/internal/dispatch/steering_lifetime_test.go": "fdd7bd4b1e52f4a20e2b9ec1b0b93ba30533f52a47e11c876b9016af3a1147cd", - "apps/parsar-daemon/internal/dispatch/steering_test.go": "38f55810a5cf5477fcdf6ef1b323d61af2a9020219a51edb61d9e7c4df4a179b", - "apps/parsar-daemon/internal/dispatch/workspace_directory_test.go": "ff6facbd137c042882d7806644dcf603a71c1e7ddc9dc52c004b208455b38469", - "apps/parsar-daemon/internal/dispatch/workspace_export.go": "76dac2fcaa69e3ad016302a8af84e3a0705c2e3801ee4d0bf73a1601c8761c6d", - "apps/parsar-daemon/internal/dispatch/workspace_export_test.go": "31216d7b324c953f604c6c426417549b963728dee98553e0d4f1cc2e69569890", - "apps/parsar-daemon/internal/dispatch/workspace_preparation_failure_test.go": "81dc3fe4555be3e31bbf2bb392ad998e1ff985a3df65fbe9bf7675c137cd0a80", - "apps/parsar-daemon/internal/dispatch/workspace_preparation_status_test.go": "eea7cb85822c7c342a5a60f83f541c2bf750b68ec377310e89f1de00c48f2e61", - "apps/parsar-daemon/internal/dispatch/workspace_preparation_test.go": "8ebb8e871fa0c494d873ec3c04ad40f7504fff9eff8c3f38a91d0715a415d1df", - "apps/parsar-daemon/internal/dispatch/workspace_read.go": "6cbb149e9e0a134c009868e5150547c3ef5410b020c43c320588f959080ae67d", - "apps/parsar-daemon/internal/dispatch/workspace_read_test.go": "23da17c59f49e042c497a1c01cb233b4be242a5cf1e3ff8fb3dbd54a07f7e634", - "apps/parsar-daemon/internal/dispatch/workspace_write.go": "2139dc3f5c3f76ff4c596272a7987e00874be12bbe49caa51fad37dff0332468", - "apps/parsar-daemon/internal/dispatch/workspace_write_test.go": "acf068abdb60074cef31d5dd364b2f0764e464a3c219f96c540307ea052f51fb", - "apps/parsar-daemon/internal/localworkspace/binding.go": "a554c97cef62c6f99f12f4094bb4b2791abc97c2d7d88d953a52117f96b351a3", - "apps/parsar-daemon/internal/localworkspace/binding_test.go": "b2ec947485729774b021479e80f7e62b5b663e866afd19c31e3c626b4c9006af", - "apps/parsar-daemon/internal/localworkspace/directory.go": "4e6d7102b59fe78086688c4cef502085920e69ba98eb991993ae0e5697564654", - "apps/parsar-daemon/internal/localworkspace/directory_native_test.go": "5b1adcb847d84b5bfbf0bfc405d1a32af58232373f3f068f2e104f349c5f7b72", - "apps/parsar-daemon/internal/localworkspace/export.go": "b2197e82bf7354dd273383c14231e838fce9bf8774bb62f580e6c4b08100bba7", - "apps/parsar-daemon/internal/localworkspace/initialization.go": "ce21698d7ee84b7bde7b4605ca35c1963cf43fec24b298ac50d0b15a9b9b12cb", - "apps/parsar-daemon/internal/localworkspace/network_policy_test.go": "5fb358dc621f5fd766feb66baee5002104e16ca485bab00eb7f33a9611b15c08", - "apps/parsar-daemon/internal/localworkspace/skills.go": "6e798a1014ae31f17a1d32a9e594ecfd5c7bd274a975baa33385b1506b216735", - "apps/parsar-daemon/internal/localworkspace/write.go": "39d8552fdf5d888d03eb17d2bb2d2f9a09fffdfbdb428d4ba6f155f124597bba", - "apps/parsar-daemon/internal/localworkspace/write_binding.go": "c0413119a73b6a8337191b8deabfbec147c31488fc913d3885e5499729a24354", - "apps/parsar-daemon/internal/localworkspace/write_test.go": "2dcf24d020d25aa00f11bbbe908632b1ce9432a26dd7fb39dc3807fbac969ce2", - "apps/parsar-daemon/internal/paths/paths.go": "10aafaef252867ab09adf5ff72367eef66f9a148c94e16c43d9f3b6ed9b5c98b", - "apps/parsar-daemon/internal/paths/paths_test.go": "c4079d9f3025734aa609e6beb258af15fc50ed24c60dd00a56a2a14edbe411ed", - "apps/parsar-daemon/internal/transport/bootstrap.go": "22d86e28982a35148e87542879d43e3b896ad415c4e2fb82a07d979f37f9ef90", - "apps/parsar-daemon/internal/transport/bootstrap_test.go": "5aba77c5b9ffa1ff66bbb8321213c76f0cf68549071a7e917f443dfc7fc81752", - "apps/parsar-daemon/internal/transport/reconnect.go": "ba0138d423c001d483ad3e828da38afb4ed121cfc9df030debcca6fcdac15360", - "apps/parsar-daemon/internal/transport/reconnect_test.go": "70bb51184ff5768fbc2a0e25061a254c5eac98c6eb4e5f1cfd154dbc06ffb29e", - "apps/parsar-daemon/internal/transport/ws.go": "4c08b38b348dbad2d77a18c3909c192784c06589a09aadc5da6165325757a162", - "apps/parsar-daemon/internal/transport/ws_test.go": "b81d95b979e664546e295a174f47d3b6af1184af6af3411888109eb4ed81f4b3", - "apps/parsar-daemon/testdata/onboarding/main.go": "f509e768e8b131fe6ed00503d37e574cf7b875eee1b2ac757316a828bc287ba8", - "contracts/agents-api/README.md": "ef3f5061f4fb0edc1010edb413d823b8455fb6e75ee73a82a849ddc43ff84fc2", - "contracts/agents-api/environment-files.md": "a14a291bea2570b829dd457ee87d43f40c12008dbe3c723cd32051be08e0e284", - "contracts/agents-api/environment-templates.md": "8e1435a6b352f70b240d3f66401565625459ed8a5cd93011d5fb91dd03fb6c39", - "contracts/agents-api/environments.md": "b9d9ece2ad3779adf1829ad35e70dd8021efc1c364ed90c5bf38038c5e39aea1", - "contracts/agents-api/harness-onboarding.md": "4030eef0864d98187b85eee0490eaee0cd72195a6d6aa54a9c90340358a1769c", - "contracts/agents-api/harness-selection.md": "1b4946b0b4eae20bc2214da4fed2099f3a05f081ddc0beff972aae885699472f", - "contracts/agents-api/harnesses.md": "1c1a1fd5ca7dfc704997bc3c8affeb5607139e6f95a4d4cc8c6a749bb3f6bf62", - "contracts/agents-api/mcode-workspace-v1.md": "7de34ce853cbf4cccb43b3f92a778f63b7bd07d647e537aaae5bb03e1e066674", - "contracts/agents-api/model-execution.md": "43a232e57c763887db4e3aede2d15174349e2b8d8dd1b9150af192c91259f3bf", - "contracts/agents-api/openapi.yaml": "468cefdac03a5a1218ade3cacca27b217c178252ae57da3f5a4b702cd0e6e455", - "contracts/agents-api/source-files.md": "64795ccf43eb1f353cc75c0e8c67ac1d1cbbd8edc12bbb312ac0e860ea0d9c2e", - "contracts/agents-api/upstream.json": "91b8fcd0999b179812029f37a580f02ca30e93b68f2d75b8100c79257eba7304", - "contracts/agents-api/v1/agents.go": "aa5019b59f26145f2276ba0b5608d6dae6892150357269c5a596b186bf139086", - "contracts/agents-api/v1/core_extension.go": "48ca9ac91a66a9e7641c10d05494dae1c45b68e39cce13ddbc3c120e8126a4ad", - "contracts/agents-api/v1/credentials.go": "67fbd02f12b0d9d3e9c97c12022ad50641c2663c0fb58f1ff45c8c6bcc53d16c", - "contracts/agents-api/v1/environment_events.go": "40146d3aa481569e1d5f6ee74a0586401c3e9652ab6cf4589353dccffccbf405", - "contracts/agents-api/v1/environment_files.go": "3364af726de028f65441ea44f2c473720b9a4868126434a31d52f7d862050f4e", - "contracts/agents-api/v1/environment_templates.go": "fde8aa379b5e6caa98d5d189ee58839ba6b476c0e1d0f341685ddcbaa5eae487", - "contracts/agents-api/v1/environments.go": "0d767cc4f1063c23849908b1ce049c63bc658ae31ce2d7db56bdc0e4df4106f5", - "contracts/agents-api/v1/events.go": "29d53335eed4b0f3a905c52ba940ca655d66010a8556e514452c43c691e83ba0", - "contracts/agents-api/v1/function_actions.go": "cdbe6c7d64c0186a12e7d41eb074c33d34b83fc34e1a174804f26488742e2c4a", - "contracts/agents-api/v1/function_tools.go": "73619eb7730fdaef3cff1a41e319a725609fae1b261c94e425e91273320e133e", - "contracts/agents-api/v1/inputs.go": "53e47f1938e4f41d0994490731aee4fa7c4a7fc3865c9279ab3f1fb8fbe44c59", - "contracts/agents-api/v1/items.go": "00e9e70e5bcd5ee5fb916307f64ba4ead341b3b0d739b19bd783994ad07180de", - "contracts/agents-api/v1/mcp_tools.go": "86b3c09108b100e682d79111402d70bdd2907ed8f4c5a17e81d33824f8e0eee8", - "contracts/agents-api/v1/model_execution.go": "83e555c918df097886ba2db46880b2def6906d95ae024865f86214f7a49f71ea", - "contracts/agents-api/v1/model_execution_test.go": "3007a6236b7acc58aed3b92a09683077e1a97180b45de70a02183ef2cc9ec2d4", - "contracts/agents-api/v1/required_actions.go": "ac78fac74edfe62f6b2fc16a708cc5c297483256a51bf3f879bc3fd976d2cf87", - "contracts/agents-api/v1/required_actions_test.go": "24e6a2d36d6bf3242542f87fd7701e5f10976638a095d5c82ae82b6ca77ac466", - "contracts/agents-api/v1/session_artifacts.go": "b9c78fcab78c898500aed133a01f70e4afb9f9c47abab0eaea4b8bc6cbf89585", - "contracts/agents-api/v1/session_deletion.go": "eaa09239de81f060b6e4ab5c2ec95c8168de0eb16c104349dc883dd08d854efc", - "contracts/agents-api/v1/session_environment.go": "3e3d62b20c3fe4e96d8f8de9a8e1056c2a6fd48fa4d602acb5a661cde15e1a0e", - "contracts/agents-api/v1/sessions.go": "2b579e01ecd0808f40df2e28e2c55cf6a99f660daf3c0f73d365a432e19ebc7b", - "contracts/agents-api/v1/source_files.go": "f34afa177cc5e1d0972bf6d45ec241f76e1024e8a3c7a8638f7bc9038aaae4b6", - "contracts/agents-api/v1/turns.go": "49893df33da1ae9ccafbfa136eece6b645d8766ef7c23231ac90f03ae3a0376f", - "contracts/agents-api/v1/usage.go": "033da3e1ba3fbe520bc8301594a1335811aa91ba9c036db82103ebd870d63286", - "contracts/agents-api/v1/vaults.go": "a8df6744de5d5328e2e9e2ee632b1dccb3a481678cb26848d498bc55ba8f18ef", - "contracts/agents-api/workspace-placement.md": "42be22be4d74f6cfa547c611d906756426e8a1e32ee5a29ee48c630a199ec6cb", - "go.mod": "843222d4d70b15598836218d50ebd1247f36a29bf4093c01369f53f0bf45a275", - "go.sum": "08faa841454b57be66a6b032bf4fa4561e873b852ae7abcf5d1fc6e9fc4da25a", - "go.work": "e798b3a7fa207e6ff4652ce799d26485c11441b7364675198b4b6f8e2081da43", - "internal/agentdaemon/device/credential.go": "9ed555738f84e37887582c36524eddaa84db65b9af6c8e2da8d0d3960aa77e15", - "internal/agentdaemon/device/state.go": "dca2daefeba1203a61e00e444b46c0b3e8207ad66c576956a6a104f2c3f7fabe", - "internal/agentdaemon/gateway/auth.go": "fd9bf38cdc1e5ce21931d7a50cfb490f6f630ff57f9edf2c82f5e3a76bc9b375", - "internal/agentdaemon/gateway/auth_test.go": "e358e165f1249ab1b222b4c9a74fb56e8cdd4ca31093298145aad49385e24a39", - "internal/agentdaemon/gateway/functions_test.go": "cc74ef848b5ae4ba3ab5714859e062faf5dffa07f7cb248400fc9092b6bf55d9", - "internal/agentdaemon/gateway/handler.go": "cbc50cf18003124b8f44bf4866ccf918303497ad10658249bbb3dd0120ddc6f1", - "internal/agentdaemon/gateway/mcp_bearer_fixture_linux_test.go": "aefcb7852d0502595570bd134fd03794d33d0b8b063aaf33f05a1e1cb232cdb8", - "internal/agentdaemon/gateway/mcp_bearer_live_linux_test.go": "b0b71ef4bdcb6814da2c10b99fa8d8c7c49770ae5cd1648909b765fcfe204112", - "internal/agentdaemon/gateway/mcp_bearer_process_linux_test.go": "7207be37502784d133b0e100c4a00459bc4634e4efde162951e4ac7e22b11440", - "internal/agentdaemon/gateway/mcp_test.go": "4916d4cd42378cc4203b2cf5b19dc36a7ec66704cc129a9af0b74fcfa07b3c7c", - "internal/agentdaemon/gateway/owner.go": "4aa5bd3ed89c5b10e5b8ebed15240873cb7dd77a6ab69dd9824833988d1350a0", - "internal/agentdaemon/gateway/owner_test.go": "75c5779743e8c577694403a8c96aa3ce4944480da95716d43d1eef0065bfad24", - "internal/agentdaemon/gateway/preparation.go": "70ee097b3c7645f67151fd39b4820f47553ab2af80b53d8a12b07c86eca4d59d", - "internal/agentdaemon/gateway/preparation_test.go": "cb6fdf929d6c74a4c969dd84b96649c71a9b8b6ab05918353cfd934f5af97b10", - "internal/agentdaemon/gateway/registry.go": "64b7cd9b9806a026ae0e98de1741698aa1bdc859d890fc0b2823fe977b602299", - "internal/agentdaemon/gateway/registry_test.go": "73a8ec2e9d9e82adfa831cea6dc2dd57c7c8bb3f5df7c19784e18d82c9f5b45c", - "internal/agentdaemon/gateway/routes.go": "2cabf2fef9da26b8ff798125a5bd72752658499474ecc6c2b116c7ea038cbe21", - "internal/agentdaemon/gateway/session.go": "099f34cc1cdf2569c8a4bd403cfb83a4e8f242936b981ea9a475d072f4f7d996", - "internal/agentdaemon/gateway/session_test.go": "8b1b65d3929b3bfd4c50bffad5b8a1d2e24bc99b5590f12cd97cc267c912198a", - "internal/agentdaemon/gateway/subscription.go": "7bb110aa071094e9ff3573a193fc13c01c76be9c12af0e43de70787834905a3b", - "internal/agentdaemon/gateway/subscription_test.go": "0f2a02fd9fdb5cb348bcd5550e4fa563113c0a96f504d1882851b6dd20929c29", - "internal/agentdaemon/gateway/workspace_directory_test.go": "52eb2a28641f92ac1a779e056d08e34b404f6f5d4a5496594edf4a1cc599a62a", - "internal/agentdaemon/gateway/workspace_export.go": "423dfcc0a1b6acfd1885eb0ca7191f0c7b766d0811f42a6210e7cb5ab8b87e43", - "internal/agentdaemon/gateway/workspace_export_test.go": "c676ac6479a02c8df6a1ef3bd120493af23499491909d372a06d02212f682be7", - "internal/agentdaemon/gateway/workspace_read.go": "9b1e7ce17bedcd5b5153415b8437d2d21265733f8df3d3548d1a2e6c1da3a554", - "internal/agentdaemon/gateway/workspace_read_test.go": "9ec3640d4df235d24b06760d3a49599680d5da642b73414770425462386d4d05", - "internal/agentdaemon/gateway/workspace_write.go": "4a5e0c313fb0664034a7bb39baf296f06fbc6c612643c8afbcc87b7e21f769fb", - "internal/agentdaemon/gateway/workspace_write_test.go": "b834524aac1d17381386abbdc25b79cd817f72afb267123940ab5477c704a720", - "internal/agentdaemon/placement/controller_linux.go": "0b38e2c31514d3502981f1024cd5b2959cb3e40deac2a28c41ffd5ed94c3f647", - "internal/agentdaemon/placement/controller_linux_test.go": "40a1c93e62fc34eb61cc9dad6e488fb933b9157f73d7b2c7446ebfdfcfc3f5fd", - "internal/agentdaemon/placement/controller_other.go": "59423534188390def224902742271f8d778ebe3aa69a8ac82086072748365eea", - "internal/agentdaemon/placement/docker_linux.go": "f696bc6afff02077429d210a4a0fd09bec3857fa40cbf7726206fd981ade947c", - "internal/agentdaemon/placement/environment.go": "be0f2b8dbd9a853120b7d2b7c6878c4bedee1dfa89cb079d3b7e23e126fb5550", - "internal/agentdaemon/placement/environment_linux_test.go": "6a9a07791c5fa19430917a226d17c099cdadf3fc685afb92803cc16c99b63615", - "internal/agentdaemon/placement/mounts_linux.go": "cf718742ba25ff9d28549edf83aa26ea36681a57437713e732bd44117424a819", - "internal/agentdaemon/placement/mounts_linux_test.go": "4670a48ec466c701812c7dc72880b292ca9bf53ea04b075b88dcdf6266ed8fb5", - "internal/agentdaemon/placement/observe_linux.go": "7883ca12910bb6b3a8e965d4e8fe30b2e618ba5a236ab5a4dde68fe115340cf1", - "internal/agentdaemon/placement/state_linux.go": "3567b9f88cb32dcb4fe7415409f891b4e30cb4418ac80b631f5779b5acad7b80", - "internal/agentdaemon/placement/types.go": "92addb1579a153bbb980080b817025d71f1ae19f2e109b7cf770ae0c9338b9eb", - "internal/agentdaemon/proto/authoring.go": "8de34e9f4a7bdf0eb05eac081caba3ea7eefdccf76eeeeace58d0f59851d8668", - "internal/agentdaemon/proto/command_output.go": "361ad80c3634c20f1525f27e1714715db7d14da2230698d3fb4af64a4ab733d6", - "internal/agentdaemon/proto/envelope.go": "bee6d1c9ad90686d03d79b87f1eafa2ea66859d7a09a9e34f035f80517aa27d9", - "internal/agentdaemon/proto/envelope_test.go": "b6a5f29157b32c0b17e1bb5fc8e03edf186f316c5d9fa92923664936ecfaf794", - "internal/agentdaemon/proto/environment.go": "e7c6ac8c5c0e2f2d6c9115f171db86a9be6a6833e807493d733e3240323ecb87", - "internal/agentdaemon/proto/functions.go": "6a375e734f63f88049dcc877aaec8051c35052ddde9b518415a97153f74e4871", - "internal/agentdaemon/proto/functions_test.go": "199e003d4836db49e717012007bb8da52e49fbc6b4d95fdebeda769125e94a7c", - "internal/agentdaemon/proto/inbound.go": "6546d89ac6afaa0ef9915350b07bb850b0628e2cb0738e83372d89bad053393d", - "internal/agentdaemon/proto/mcp.go": "b288fe66ff902c1d957f8394a4ffed74be8ddec3f3083d8b28f988a1c50ec06c", - "internal/agentdaemon/proto/mcp_test.go": "76dfe3f83f878eb1ef97ec975653cddb37fdfde7185c088ab63400bc37f05117", - "internal/agentdaemon/proto/outbound.go": "ba0347407136d2667eb2bd70b1ad9bfdbf9829d7386bed5f58e196f7e790d18a", - "internal/agentdaemon/proto/preparation.go": "06356bfe3894beb7f22916884a33cef808eb558830f64cdaac8136bfc614d707", - "internal/agentdaemon/proto/steering.go": "ee321c9878d35d18a2df4e70390d2c3e9c6a7f428dc930cba68491b409773212", - "internal/agentdaemon/proto/subagents.go": "89caced5e7b48fbc8c19c71f4b5f24694392b45f90cfd76cffc390ccd29219d4", - "internal/agentdaemon/proto/token_usage.go": "34bb7ec0b8feae065f8e886ab5bdacc29df9237704b8b9f500fa77459b73e30c", - "internal/agentdaemon/proto/token_usage_test.go": "7e6da302af6953247ca44cb6b28f962520f5479c636f44b68578bf285d418fd7", - "internal/agentdaemon/proto/tool_observations.go": "4de14673a4f68bda80cf95e74d9319d0bd6a7a2ce3bab26c98f4fa7e1f8934c0", - "internal/agentdaemon/proto/version.go": "8af45f9d8cee906487a57647dc24d3b8358c205bf5d62fc32b0536024da26750", - "internal/agentdaemon/proto/workspace_directory.go": "8939c5d86ac718a71cdd87b0b2ffdd57b3a3387cad6af45f3e1211521d47ff54", - "internal/agentdaemon/proto/workspace_export.go": "c2a4f2fe228d641e1a51cbf8c2916e04382ae8287aafaa2d3fb6af90917a8a50", - "internal/agentdaemon/proto/workspace_read.go": "7e541ee6fa8c6dfd32a506552b540c8805a340a7ebccf2dbc61bce334e922f7e", - "internal/agentdaemon/proto/workspace_read_preparation.go": "ed99a8fb25a692be6df712af49997967d2a1e885b813f21496b2b404b0ba18a0", - "internal/agentdaemon/proto/workspace_write.go": "e22d75afdcf2e5b0234c9bf5aef1fd1e4bf1a58fa16fd5d7c048e0f39fea8b3a", - "internal/agentskill/bundle.go": "9da0ea7cd7b1bb22b33c2eecd0b8683e3534b671d4829118130eb67fa034e294", - "internal/agentskill/bundle_test.go": "8ea6a3ccbe4328a0b43bdf31b3e7f4a264cea703c15d1caaf248ee49bf693199", - "internal/obs/log/api.go": "bd3309df30357681860aa7807eeb7c84f606f1d376e38916c5d6deb5e359c12d", - "internal/obs/log/api_test.go": "0816731c4da1e32f835422529aa34bb775372fd2a211bcaf60184bd7f5a02072", - "internal/obs/log/background.go": "9f0d2a0ffec5f19334947ec1f67074851c7365b515112fea1873881b435eb4e1", - "internal/obs/log/carrier.go": "32a3420becd65238fcd01ea5766c55ed222f635f85da0854faa52df37d20fa6a", - "internal/obs/log/carrier_test.go": "83aa90da768a7f48cd7be5e1455664add09f79fd5c8eeaefbd56361175ee6149", - "internal/obs/log/context.go": "1bc395ec5daadc67d433341e14e4db6d24481c65bccdad664955bc8b7512e71c", - "internal/obs/log/discard.go": "ece9cfc56d7a9edf9210f559205284937666574a154c1cd92d07882947bdd4e0", - "internal/obs/log/handler.go": "dcda4cdcadc1920f816961f5a4fa6b527b3ff1152c4b639e5e045d2f6cec9323", - "internal/obs/log/handler_test.go": "75346466d0c05a3e8e98243f38066b9eb05d967f47aefac6682daed32478fa1a", - "internal/obs/log/http.go": "a2fcd9712a7761a5c0f7a6647d366aa8c4e44650e14b76eab6eeff9e963ea58b", - "internal/obs/log/http_test.go": "86eb8cc79e0067432d1be9c81449fbd62ff2f71b670d21313817f7e4ca8bca07", - "internal/obs/log/init.go": "d1424250abf27661d86283ef2d649fec67359a79d6850cc4303fb3ed708dbd8c", - "internal/obs/log/trace.go": "9de780021a86ef24a5bb9a04e5ecccad38b5f97484bbee2302d68541f77d298c", - "internal/runtimecrypto/cmd/emit-fixture/main.go": "3c830afbdadb12d647e3bc0ea4cabbb09e52de4414986cadf625ef46d8e9355a", - "internal/runtimecrypto/runtime_seal.go": "e54cb29f65a9e4f3642a97de06d856b73bc09ee286a93032c69a38a558d42d11", - "internal/runtimecrypto/runtime_seal_test.go": "08d44c5848c0cee89b41b41f813866321ea05beadf81950804a139c6993696f3", - "internal/runtimecrypto/runtime_seal_wire_test.go": "d764c59191d86d03a049c3588fbf6609148c55de0cccc76a5b50adad93deb310", - "internal/runtimecrypto/testdata/wire_v1.json": "cc67df0d258d7c7134441460d6b12b512ad03690477b157b077879a2f26f0702", - "packages/agents-client/README.md": "76567ce62df606be5c2ac98e97db00215aa98274a3f5d192f465b2ffa46f49c2", - "packages/agents-client/v1/client.go": "694caaa35a62db9ccf987d86e3eac1ae3b27e0ef767d155ff15ea56e92e8c3a2", - "packages/agents-client/v1/client_test.go": "b7e2d9a2d95dbf647ba58044115fd21587340e78955f7457498b007a927df823", - "packages/agents-client/v1/service_test.go": "9f98e94bcdf74190373ed00afceb4dfbbfa02353506b0c73de68410a0347d858", - "packages/claude-sdk-adapter/package.json": "f699d4383ce960708f7a9377d3dc49ad028c7eb5510f44901767cd57cb42510f", - "packages/claude-sdk-adapter/src/adapter.ts": "31590c5c6c0d031d43f3b53ebda9836971ead12977d7ef64ac12c373434965f4", - "packages/claude-sdk-adapter/src/command_observer.ts": "474ef0e90c84da72703ccec947d7ec37186a010b0b6f6834ce84658b820d6771", - "packages/claude-sdk-adapter/src/function_bridge.ts": "2e1eadcb6bfd95d4ce7a36c4551db6b9fe2125e05f8399e016730a914d2ca12c", - "packages/claude-sdk-adapter/src/functions.ts": "7f45b48cf702c6d0018b4197a99d4d8a54fb4f4f898c6adfa8bf11fed3c585ef", - "packages/claude-sdk-adapter/src/inputs.ts": "a3613e967a89a55606afb5c4e8f7152919cd6105ae2e09c1e170256becc69edf", - "packages/claude-sdk-adapter/src/main.ts": "119df91b4af73a5b6a7e662a691f886bbd75e6050c9ae98e8b9330543f0b1b34", - "packages/claude-sdk-adapter/src/mcp.ts": "baf1ee42787e95f65bfc65e4ddd525b5a02e925c728f7c903cf7b1687b31fafb", - "packages/claude-sdk-adapter/src/mcp_observer.ts": "2a4b297b5f0a8ffa491dbbcaafa8824e4067d1fedb3dc6625cf2bf6187dda29a", - "packages/claude-sdk-adapter/src/messages.ts": "c67906d67ec357f6d34e9b43a1136b9978da364a124154fc502e4f9831e6c528", - "packages/claude-sdk-adapter/src/native.ts": "81e203da63299b84c75d493a451dedc726389b0939a9de65052680c6894f8290", - "packages/claude-sdk-adapter/src/recovery.ts": "0e4ed5ed8077366c3da837baec8c7d43e970fc17836248624234ab9f7defa814", - "packages/claude-sdk-adapter/src/request.ts": "58de7081d6b95ed060ab652d137183f2a5cd68fbbcdf87771075eff4382d5e7a", - "packages/claude-sdk-adapter/src/runtime_check.ts": "48d82334149bf34d1ef66d98f4e9c3747b6b67f7c7e8eabdf418eac17e61660e", - "packages/claude-sdk-adapter/src/usage.ts": "bd595007d9dbfebdea2151526b67c2e6e5b49d25541e0e2669365e2e486897da", - "packages/claude-sdk-adapter/src/workspace.ts": "fd5be464627ed13f3d73b126a0da09578ecfffb50891035587d55f934ce02585", - "packages/claude-sdk-adapter/src/workspace_directories.ts": "ee968ba6bbb2600f89bf4c879046a1f8af7b7a676ea8691ab9e7d747546905c1", - "packages/claude-sdk-adapter/src/workspace_reads.ts": "57cbbdd7de1d84691876c6a43796f2485a72086e96be3920ac59f7847a1ba908", - "packages/claude-sdk-adapter/src/workspace_skills.ts": "522e1ab23775a29457a1a3d561ecd96cc512813ef840a53b2c1214438becf2e5", - "packages/claude-sdk-adapter/tests/command_observer.test.mjs": "7aacf6139092a73af015e475bcebd4a73057f83b2cd010c634b318d5f52f8046", - "packages/claude-sdk-adapter/tests/execution.test.mjs": "d499ac40417dc46091d178f0db7863f81ba35bb979c379bc243497cfacf9edd5", - "packages/claude-sdk-adapter/tests/function_bridge.test.mjs": "eced48c50ef826681798a980d308506b5c44a531bfed29c607aed14e65f1f877", - "packages/claude-sdk-adapter/tests/functions.test.mjs": "d6fa33f6a8c884b01da65e16a57856cb3710e2858723495193f3c5f0d8d48fa5", - "packages/claude-sdk-adapter/tests/inputs.test.mjs": "72f1985ef51e84adade355941ba64dcf9dca6c3fc883dd3b561a11462336a3e3", - "packages/claude-sdk-adapter/tests/mcp.test.mjs": "830784010c49454fd28b066799d52dcfedbd98d6952980784cd064063bd9d984", - "packages/claude-sdk-adapter/tests/mcp_bearer.test.mjs": "d2367207e250e29b8ad670321997f924f89a3eddea8eb17a941f91575b4c0498", - "packages/claude-sdk-adapter/tests/mcp_required.test.mjs": "ffac00cb5081496390f1faa34ab228ea316d45b8c8d8ec7798b1861fde7a818d", - "packages/claude-sdk-adapter/tests/messages.test.mjs": "d099b8e41553c74a73ae8592086a6d4c706918e55b5aa120b1e51c1a3545a6ba", - "packages/claude-sdk-adapter/tests/native.test.mjs": "579a89efe967d31b7e244e8522131acf330cc4cfc6872eaa2be24009a7a47f06", - "packages/claude-sdk-adapter/tests/preparation.test.mjs": "3ec7ef45dd441bb53a31c527922da16c5752e2bd54091ccaaae1f4ea625f674c", - "packages/claude-sdk-adapter/tests/recovery.test.mjs": "90d11786a771725c76bde950c8398e015b475401b080ad25b91cbd1207a46056", - "packages/claude-sdk-adapter/tests/usage.test.mjs": "9d92cf877b0bcbb1a2066d8b13d5ea34eea6fe749e4e168bbc6bc49428072e0f", - "packages/claude-sdk-adapter/tests/workspace.test.mjs": "e615b96cc30a1801bc23b5123567730a5e5e9463114d0b055e9e156b8e792193", - "packages/claude-sdk-adapter/tests/workspace_directories.test.mjs": "f1f8edf037026c8aad2442766f338274e6efabf4d2317ee830b510c7ab518e8a", - "packages/claude-sdk-adapter/tests/workspace_execution.test.mjs": "d684980f8af7e0b472f43988920dd490148e73aa85f33be590c8b5b8f8f49570", - "packages/claude-sdk-adapter/tests/workspace_reads.test.mjs": "7fb76c5b06b02d21d1a8dccdf5d7c96132cb32d1358808c0515375a68a6a621f", - "packages/claude-sdk-adapter/tsconfig.json": "e380c6712a2b1b64f1cb23458a71be0190cb6fc3ba73b44b835c5255e173ba94", - "packages/codex-executor/Cargo.lock": "2561944c74857ea1205175e906ee5ebf0865566b77583d64d74441e55c26a876", - "packages/codex-executor/Cargo.toml": "793f0243909f533326606e61eeb47727afa78cba5b300a8e960d8faac1a93d82", - "packages/codex-executor/README.md": "8fa9c85fa13b97f657b3f736e74888abe7d6a151ca9b98fad78feddea37df21d", - "packages/codex-executor/rust-toolchain.toml": "9e87a5aa3fa20f8853df37a25635f051850fca560b71ab9e2be4aa09857808fb", - "packages/codex-executor/src/bin/directory.rs": "4dd8298ff6b9b91527508c7512babb39b98fecbc40b752d85bf2864d5848ab55", - "packages/codex-executor/src/bin/export.rs": "6953ac76772bf6ec5fef8be74f8362a46eef8667d089383a2f9b9f9ebd55790a", - "packages/codex-executor/src/bin/write.rs": "b3604c54fd82e9dd388899367fb38110fa472ea6c4f7e2b6119ef7bbaa4c89b6", - "packages/codex-executor/src/directory.rs": "b12956ffc27458376ceed41f94da0caf55479fd1b92bb577b5a4f5ac823f1582", - "packages/codex-executor/src/directory_tests.rs": "d1f2d8ef2980d9bb6e6ac8b25a385be77dafcb692c1eedadfa9b32892c35de4b", - "packages/codex-executor/src/export.rs": "4539c257207c46f88f66463ccf7237d6e30031204016582a89df645c4a791b6a", - "packages/codex-executor/src/export_tests.rs": "8afca2591029d86e3ac48259d99454d769bf1b55e9c1129620c20bc74ae52401", - "packages/codex-executor/src/main.rs": "b62d1610ca958be7709073643b55840215372ebfc6b44a97a2d9b24098f985bc", - "packages/codex-executor/src/options.rs": "943ec928acc4c66ac20cb855d0061ffaa2fb5f5934a80d087986dd1ba9297b2f", - "packages/codex-executor/src/options_tests.rs": "32c5815a3f3fa5dd8c3f846a52b3400395647f596c66292c06e126dc482e051c", - "packages/codex-executor/src/runtime.rs": "2bdc9b1af0fb442d4aea37c1db9f7701c72bcd78a5c5a667f5424ca5d550c894", - "packages/codex-executor/src/workspace_path.rs": "32115a6826e9f14c45d4f4272134e6e7b3580f380e5d99635755a7c7842003df", - "packages/codex-executor/src/write_file.rs": "13a75a35467d4cfa0804eb5e70a79f36c95006f67a95c69ecd4656eaf1d8d778", - "packages/codex-executor/src/write_file_tests.rs": "db45c81bdfbcec2edff4d047a383607214f9919165b2ae7a5f06893788d800c2", - "packages/codex-harness/README.md": "53a6ae582a457b12cc869adb2c06be1fd9eb17b05f17ed388495db95985c800b", - "packages/codex-harness/patches/artifact-target.patch": "18606942555a060f4b626fd1ab5c0e7e6324f118dd13a65f28f546d03c622f1a", - "packages/codex-harness/patches/bounded-read.patch": "5a6a49ac0b9b9398b772bc27c6256eb11af64487427bc57715d7800178c2e5dc", - "packages/codex-harness/patches/manager-exposure.patch": "047e1fe5e267cfc350e0a6373e50eeff5c51581bdaffaf29807430d35b8c8adc", - "packages/codex-harness/prepare.py": "2031f326878eec5e69c5c87a5078ad250ef04d3227bed1190d51e63e39211abc", - "packages/codex-harness/prepare_test.py": "d9f4d88ebc4aae06a07339dbbdea70c4136aca839462416af7a57267d95bb27b", - "packages/codex-harness/source.json": "ce29c91923e52ccbaf312d55d2e5b5fd62f2d5d8e8ce47a8a318e9e3292dbd9f", - "packages/codex-harness/src/files.rs": "96ddf65fb772eaca3eaa5c6fea1caa39ce142317259bfb35e0730caaf3eca754", - "packages/codex-harness/src/files_directory.rs": "f0502c41a9a97ac02747b006085fd56c1a1df2453182bce8c7e4bde3cf66d46a", - "packages/codex-harness/src/files_directory_output.rs": "da5336b40f34b637b3792b24eb1c05a3b5a5f433c4d89520d5d708302dd0219d", - "packages/codex-harness/src/files_directory_output_tests.rs": "4652b1bd8b96dded2eb0608a1c054313119cf89a60250876c3620ad9c45d8d44", - "packages/codex-harness/src/files_directory_tests.rs": "973005f8a32bc92f13a2155f90641b89e3f211e36c76f9aba615395ab97348a3", - "packages/codex-harness/src/files_process_output.rs": "6ea2760bf4471924faa86fbea3fba04099c46a92d65ff6bdec3d48558be1c201", - "packages/codex-harness/src/files_read_tests.rs": "c1700d2cee67803a7d25b1b3cbe4f4fc9efc473755c0a0601eb9c2e286fbca08", - "packages/codex-harness/src/files_tests.rs": "bf8bafb2d3a7cfde07b0a3570c14cb4cbd74ff1474b0582c5f0efbdce003ab19", - "packages/codex-harness/src/files_write.rs": "b9364374f00a0122d474e6531071a64ac69d7f520c1b205e614554e6fea97459", - "packages/codex-harness/src/files_write_process_tests.rs": "5c003305fe0798995cbb0e639952092183de0a0fffa4c41fe41edc12c0c3b504", - "packages/codex-harness/src/files_write_tests.rs": "8c806b3a61a08267f3fc8294a5e450e4ef97c132f65be3e29a41344dd1db9337", - "packages/codex-harness/src/main.rs": "07beba04766b20698dd3825e93ff59c88ec7829c91a85557673970c2a00cbed4", - "packages/codex-harness/src/options.rs": "2d333f2616d126932e8553b398d141646b978c67cc95d4b8e108cb3d410581d4", - "packages/codex-harness/src/options_write.rs": "417bf1bd1ff49b411937277ba7470d1aae9e6931eeb3517ae1065bdc8283be4e", - "packages/codex-harness/src/owner.rs": "0672bcd0c8fe6c29da6105a87e37be5321b29221e8e42d3dd1cf9d959f8ea4c4", - "packages/codex-harness/src/read_profile.rs": "9ed794518b95c4cf2989ff9fc308ebf2ae04215017802dd3d30df361c2d0b091", - "packages/mcode-harness/README.md": "de1201b3464a9153225b792c9c1d395ff6a2878c9287bfe8bbcfb9dda40fb771", - "packages/mcode-harness/bridge.mjs": "84beb876b7d919001f1f4fdbd874ddc92f432fcc29e60d72cf78d3b5e7c7260c", - "packages/mcode-harness/build-sandbox.mjs": "ffdb7b97c61fe99e23ae4919fe059963e30b322c8db81cc1adb54907df693667", - "packages/mcode-harness/build.mjs": "d1087f55fec35b1070b51fda7af7fe366b1431a46becab4d7abe05192ee53508", - "packages/mcode-harness/check.mjs": "35bb2bcefa0c0e86119e1983fe25b46d7ec5140cd58119a65051968641eeeed7", - "packages/mcode-harness/launch.mjs": "ac09aa9362d9414c5a030fc90900526676b48b474f3f1905d408ba68b3792645", - "packages/mcode-harness/native-pi-tools.ts": "bd1ae8bcc13e50c7be10464f1d7676b4de8e698826d8eac75fce51ad7a1fca44", - "packages/mcode-harness/native.test.mjs": "c0eb0c7ca03006112e8d5a7795706b190cf1fe2cf22406372c5d8dd1ca8c838d", - "packages/mcode-harness/package-lock.json": "3c95204a8093aa40cd979104aca71725e96bb2871aedc6f6aa5d591b4de25344", - "packages/mcode-harness/package.json": "61ea160546c6535b60842417a0c70908937c3048ea4e534abddfc71e697f8ddd", - "packages/mcode-harness/sandbox-entry.ts": "55534a6ecbccdc8d1c6b295d9b0155eea3ab564d27a339425a1f48e7747580ee", - "packages/mcode-harness/source.json": "9d27dfe3e8c2c2d138770790ec00e55b0eb6536640c3118431cc98c982f23cc5", - "packages/mcode-harness/tool-executor.mjs": "8c18c63210396f93ffcec6aaa0b6249791993f4b018c67131b22b0eb98decabc", - "packages/mcode-harness/tool-executor.test.mjs": "1685af2f3b933aa1087cdd44d2f749b985df8e91cf615171c2184ffd7eec1c5d", - "packages/mcode-harness/worker.ts": "f321e802d4f5def1cc78702e7b38165ac038ef07e98276ecc0c0a2e334fe4d38", - "packages/tsconfig/base.json": "47bc2b459dc8bc8b3f1ef2a4063a58635b240dc3b238aa5a6e23c50f12906679", - "pnpm-lock.yaml": "1c5d4be88ba1bb16026c8d94ba2651ac03de64f92878141aab48ef83a3642e8c", - "scripts/build-agents-api-image.sh": "b42216ac6ebc59bd76d0d361da9641ae38d9fc185ffba3344f115987c528ec47", - "scripts/build-agents-api-release.sh": "50178e892135dd3bd195e0012ba852cd8a07113c3c7b0ab55ce6f16ba60d9a19", - "scripts/build-agents-api.sh": "bcf43f1de3c2455858a2d03046ee32440375bd10e7475c2d694e6597b47e05a1", - "scripts/build-agents-executor.sh": "c2de322e5ab8db82811e44dc5d8a88fe3deea5bddd20922dc03be0f40d152546", - "scripts/build-agents-harness.sh": "9d56a8bc84c0caf1032baa5c71b64bfe6c2145d6618250ca7d1c5b6901945ec7", - "scripts/build-agents-runtime.sh": "611973bbb8b191fcfb2f876ffac59e476ae5a4dab42f32a0db8ee9a3a0b06c6f", - "scripts/build-claude-runtime.sh": "fa54e5e2b2faf2ca96381b98ba0a6b5b762c50d7c2029a90b51dabae5cb932c8", - "scripts/build-claude-sdk-runtime.sh": "1af711ac51d042bcdc164baabc3aef11c9d5783f19ed0ebf84f9c34fb8bcdb18", - "scripts/build-mcode-harness.sh": "b6c7be07467de5590cfe8c1a5bc5b8a1f389e47485c7201c7462d916b88ca486", - "scripts/build-mcode-runtime.sh": "7ec993fa5331477097a01d522f3eb87363c84c4090c93ba937f82a916e47a097", - "scripts/check-agents-executor.sh": "230d2fdef226bb196b85949a0ed621d7a6b00311f2b8458b386ea69b4e0d5180", - "scripts/check-agents-harness.sh": "d4ea236e4227b4abc08ffd5dc36e66a1c71780bb096e623ce348e374eeef09b0", - "scripts/check-claude-sdk-runtime.mjs": "863a7f3ab04ac36495cdad89ccf480b8559ce0ce218b37b0706653a0f75c95df", - "services/agents-api/CONTAINER.md": "1de4d9b5e9e118e3766ec240d52cae29081eab6f308818f3ee54d7969672aed0", - "services/agents-api/Dockerfile": "023c0c920a1f7070e4b12678469f2d590788e96253bd180a6273deb5f284d45c", - "services/agents-api/HOSTED-RELEASE.md": "bcbd38c30a47d5edd89c1cc77ad626f9d1f9d67f379e6dea422227ff4561cd18", - "services/agents-api/README.md": "5f5815fa3dcacb73a0cc841afe346383be2e98f85f31a0d3b24639c142d61c85", - "services/agents-api/RELEASE.md": "4bee7a0fec404dbc0260ed12c20db872bb1e69c53dab95148b7d840eb522d66c", - "services/agents-api/cmd/device/main.go": "8ed7956b0330455d546f682ecb59341926468ab7026b1da0bb6dc45a997c1e5f", - "services/agents-api/cmd/environment-key/main.go": "57fddf10b7f40714a24805d1cb0d9f862114c38b7c46d2006efad5e0c6813903", - "services/agents-api/cmd/environment-key/main_test.go": "b596e67a6ad535ac1b75bda0e9a1c09f5708d6f84e56a547ab83a3362adbe024", - "services/agents-api/cmd/migrate/main.go": "8a313071b1e5d093f92dd3c771942933cbd848199772ff3203184eb08120c151", - "services/agents-api/cmd/server/credential_cipher.go": "f1dd68adef18ddf6f60b7487029bfffe7e22267142c34ab3cec08a71eb2abf15", - "services/agents-api/cmd/server/credential_cipher_test.go": "a2ff7a03e98d3735581de60e535b3e1fe2c5899ff2afd6dd9d58914fcd01ca8c", - "services/agents-api/cmd/server/environment_connection_test.go": "03a924011b4367880fc20e70446040221f50249c0f867c599dce9bc686f43fa7", - "services/agents-api/cmd/server/execution_options.go": "cf70c0ec3e476470c2d5d3174710fc295185e5e7271b74a2de29162b14a8fd90", - "services/agents-api/cmd/server/execution_options_test.go": "5b69f8c16d59a569b08d59743e8f5d2c26b0d45870d24b283b10e9bff9e79458", - "services/agents-api/cmd/server/executor.go": "8b7efdc104afc163531479f0f6ba135c27bdf78c4ae64cb42b36244b8a322db6", - "services/agents-api/cmd/server/executor_test.go": "af0122527674c641a45fe1ffa32cfdb9ddcd0d6de05eb4eafb0b10cd86ffa63d", - "services/agents-api/cmd/server/main.go": "aeb466a1dd780d8eb4cee96b9c5373995773ecc7b6e52bd181301344f7702ddf", - "services/agents-api/cmd/server/managed_runtimes.go": "187afae94d8c355f2289cf6d325c6d9755f7d7ae7f829ae68f462b6880445e64", - "services/agents-api/cmd/server/managed_runtimes_test.go": "581860480cef8ed307dbbe2c369f2cebddb32ee0fa3b788b128d1de1d5488247", - "services/agents-api/credentials.md": "35c42962289b393e8fe0649c19c2e42ef8d8e51ce3b1dee788e5c0299a02d1c6", - "services/agents-api/deploy/claude/Dockerfile": "b41ae6a768c25cf44530cdfd9f52755c2a4440ace5031c2414748cb0d0feba3d", - "services/agents-api/deploy/claude/README.md": "3b904f6bb720df09e90dcfa7542315c5510a776b94bedc89b275c0cacb54729a", - "services/agents-api/deploy/codex/Dockerfile": "d7512c312d71a06a969187e6297beaf2fe70404d22bed6999f9b3f2422c3a170", - "services/agents-api/deploy/codex/README.md": "d8771adf1ef29284ba75474333dcaedadf3170fb68151f4db8bc4309220785ed", - "services/agents-api/deploy/codex/requirements.toml": "25b1cd3a21ea22abb10dac8f07a502c6c6091bc907c888ac47c1665fc8300cb6", - "services/agents-api/deploy/codex/seccomp.LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", - "services/agents-api/deploy/codex/seccomp.json": "7bbf86d803329c57ee48343e0ad5eb600fd4e924533cf4274dbeef8b884554ba", - "services/agents-api/deploy/codex/tool-env.py": "0d96eb06f63c9397b1b1984219fff3851282165947120a9448194ee8e73846c9", - "services/agents-api/deploy/e2b/README.md": "72c9610afd84c2bd0071b034d39c7267faa7d1ee2e15082ca22607282d5a6063", - "services/agents-api/deploy/e2b/build-template.py": "7f7809afb5a12db76622ca941d7af26933d0975befe97a7ad6c85579fb1e4cd8", - "services/agents-api/deploy/e2b/init.py": "4da05f08944b931fa4c878e93334053c9ed8635d2bf769439a17f82d5c187e87", - "services/agents-api/deploy/e2b/requirements.txt": "6254e2e3170556b8155633983635cad630d781e79c3f0468bf582bd1694ef1e0", - "services/agents-api/deploy/mcode/Dockerfile": "1c9124f2c42ca41439373eec1fb416d0ffd6f7f7a7806f287ad5a33c6ef7f16f", - "services/agents-api/deploy/mcode/README.md": "48a77fccc9052ea87520866442bfc9b9839d902a07d864022a5d594db9023682", - "services/agents-api/deploy/runtime/build-system-seed.py": "c3ccaac47f711a5eca73f364be66d9e8f9dca09d90195704d1eef70edb6ec828", - "services/agents-api/deploy/runtime/initialize.py": "796529475765f349be1ffdce22cbf3ff1d313dd29c0756f1f306a3d2773ea1d9", - "services/agents-api/deploy/runtime/initialize_test.py": "076268e55b68119feb8180486c6a90ee1440155009f3c548b8eeded99649e043", - "services/agents-api/deploy/runtime/tool-root.py": "57d53ed91b562017e21b29a88a57d9523191525c04c44ff7f877ffaccc8f9cc7", - "services/agents-api/internal/api/agents.go": "5b92ae68f16c8008d106a7c868fe8a0e877cbdb8f754b990a89a435e36a45701", - "services/agents-api/internal/api/agents_delete.go": "ad6ce0db1d242dfacde623647278f2de78326f2e45966f3dae2d5babd070c608", - "services/agents-api/internal/api/agents_list.go": "a51f2a4386f9dbec2d566ead591adacd17206e4d05a71cd123244861cf18f830", - "services/agents-api/internal/api/agents_update.go": "bfe80e3f0e0c0f3a9f61167e1316be6b16bae55fb0edb35a62b9d80d34ed19d5", - "services/agents-api/internal/api/auth.go": "b93524eb185ecf07ec25d5eb6ea2f2ceded53e015dfeb81b7d1f064e43311c4c", - "services/agents-api/internal/api/auth_test.go": "3d6e322bf2a94d0ba9196e8cea5a81949d86eb3367e18432f633c9f093c8eb91", - "services/agents-api/internal/api/claude_admission_test.go": "4f9aa4c77ebf4d3c19b33d25cb1b9b36d1a6c78cf132413bc0a423cd475528b7", - "services/agents-api/internal/api/claude_mcp_test.go": "0342c879f755b7f42cdb584fc32922541d9c68a7b08085523aa33e2c2af747e4", - "services/agents-api/internal/api/configuration.go": "c12900314a5640aa8009e8db6e3d2e9680c60e0f92b4e5fe5ca88c417d4f93bf", - "services/agents-api/internal/api/credentials.go": "cd5345b9d2106af6fa26c8d5b21b8f7fe426eedf515f40edc69e6b684f807db6", - "services/agents-api/internal/api/credentials_delete.go": "c15c19c0cf60f35e3a9afcc449e007f64353e5fc4621a2f287db5f33e7c16fa0", - "services/agents-api/internal/api/credentials_delete_test.go": "b2e0155ea6f4ec1318e0659527be878fc0769a2b8f2ea389fd597c3c9b57b966", - "services/agents-api/internal/api/credentials_list.go": "c082b327a440d1c897362cf58ebd63d160c366661b6bb9bc7e4f25fdc65d4d2d", - "services/agents-api/internal/api/credentials_list_test.go": "7d91346664c3d65fe8a32ea3e5242148ffc4a58f61d61505fe61c0727b02f287", - "services/agents-api/internal/api/credentials_test.go": "29c9bbae7c152f32a7708506e053c814383523d203b210ac641f8bf4ece910ec", - "services/agents-api/internal/api/credentials_update.go": "9b9a27a1aac75454a20eb438da5f34a1565fb5243b0431b70a7e49e9737c2ecd", - "services/agents-api/internal/api/credentials_update_test.go": "2143fab6bc14ad681f085ad51a8efcc6cfcd6f3d26515e7a72505ab389d6b2cf", - "services/agents-api/internal/api/environment_creation_test.go": "e55c6c96d00773c9bd14af6f573644aa5fbf7b8d62a5b5f1bf0d70cb27b4323b", - "services/agents-api/internal/api/environment_files.go": "e6dede38245ae3205e6874e52c21e5b719f405a61f9aeb7e3e2d044ced48de81", - "services/agents-api/internal/api/environment_files_completeness_test.go": "bf631b0c9eb1010ad5bb6d5ba8752152423741ca74af49fdb7035714bd34c9cc", - "services/agents-api/internal/api/environment_files_create.go": "183b96a3dbce0604ad5fa0c9daf7a0a44a143705e4e7d7dfcb64bf926eaadfd8", - "services/agents-api/internal/api/environment_files_create_test.go": "20d6c9c2af4511969c92b8054b6667b6a1fe94745a48ed4178bc29ed43dbfffb", - "services/agents-api/internal/api/environment_files_cursor.go": "87bd88f03f6b076a0c5b177dd91380c3cb50275b16625c6848578cbdf13ec88f", - "services/agents-api/internal/api/environment_files_deadline_test.go": "5d1261a5394a5eb10e8c22c66eac9e1934e94dc4767ca51fc001644105497dad", - "services/agents-api/internal/api/environment_files_query.go": "d52dd7af7c48a59a62c6d691ce061c86d62cc4c583637c765eaf7db8218bbe10", - "services/agents-api/internal/api/environment_files_test.go": "c97b84729d4e2db0b0a233bfe8ee8a86411c96e0c3861dca1d931ef355b5ee63", - "services/agents-api/internal/api/environment_input.go": "76c69aee7408a69996fe176ec4aa8d35b5b9b1f858d49519cf98419a119c8bf6", - "services/agents-api/internal/api/environment_input_test.go": "a770331dbbb0f002b3d2f644435757a58bcb3797773b6586a70f38c812f44729", - "services/agents-api/internal/api/environment_request.go": "32a14f9f2b2c3e4472b67f4142bb92579dcf4f2d8e640882ed801d937fb5a083", - "services/agents-api/internal/api/environment_setup.go": "d895d4a3de7316fb57771a90cce17242a364c99f246f0c2dcee7f49dd05857f8", - "services/agents-api/internal/api/environment_setup_test.go": "423ee58b065bb13890a44bea7cef75500000c7857eba9058a84a85aa6e6910e3", - "services/agents-api/internal/api/environment_skills.go": "24d2fb7ec44553fce1d95ac1ba6f6d97c57f832c32abbcc09a43d489d642c889", - "services/agents-api/internal/api/environment_skills_test.go": "c2598e18f67740cc2075b1621158cf515ede949608e92b73d06f48e47e9c51e9", - "services/agents-api/internal/api/environment_templates.go": "d55e6c738133558c300793209d98ade9191a7e2eab19cf0b6931a073ef868d0c", - "services/agents-api/internal/api/environment_templates_test.go": "8d7ee3001daef4b9dfc7890c995ab240b328535d003f8a6b274ea3d4ce823113", - "services/agents-api/internal/api/environments.go": "0f6cbb7473032778c9c7a3f0893a62811a2342066dcff5bdae99ebf296b446fd", - "services/agents-api/internal/api/environments_test.go": "106b9e1377cad4b64e5b8cfe56e6116f19733103b7486d3be423eb4953c37ffb", - "services/agents-api/internal/api/errors.go": "74a9015687633a9f97bb796b0015157f221a8620b050859d550d7205470ec873", - "services/agents-api/internal/api/execution_policy.go": "a1e3b0193e6d480ca1574949e297f186cbc575589b13d8c4fe292476b6170f9d", - "services/agents-api/internal/api/function_configuration.go": "17fcf528de3dd50082efbc5f8196be31148a39612bf74c46382e4fb2429632c0", - "services/agents-api/internal/api/function_configuration_test.go": "d138243b79e9ec744e94f194d353d1791c59e0e2bd8dda9c98d6656764997ee5", - "services/agents-api/internal/api/function_inputs.go": "05beb845d973a42c8d8a76e4e2b1bfc270d7a117f5568d3df5f339cf71c1f160", - "services/agents-api/internal/api/function_inputs_test.go": "8a29ae33d998ae27f8d139a6a8203ad8d70a7abc6aa84fb796313bf01121dec9", - "services/agents-api/internal/api/function_state_test.go": "4a2148a45a6605c2789697b84a69d43afac93314f96a4f87ee89842f0b752ab2", - "services/agents-api/internal/api/handler.go": "275101eb5ff9cf13d01208b33645fbb07135eb219402ae72107a2dfd0448a3e3", - "services/agents-api/internal/api/handler_test.go": "c62e7fe79912afe62e901fc6ea6a1c80b9f598ce098626ad1d35e6a22f4b8289", - "services/agents-api/internal/api/harness.go": "09ea5bd698503826f4d1ae7eed52008715e2052ff68e1c491518f1c3432efd43", - "services/agents-api/internal/api/harness_test.go": "7c37426431b6f3c3d2097badeeb15ae341505ec6aa0df48b1d5c05744fe4725b", - "services/agents-api/internal/api/hosted_environment.go": "e43fc7f01eabfebb4a6e1de69bab3c7a82aefad5d1f1b5877af8b40bad33cb00", - "services/agents-api/internal/api/hosted_environment_test.go": "344fb03ac8c256f74bbcb1eab9fd3ba2aee2d251c5fb73094ed30cd5a7e6c075", - "services/agents-api/internal/api/initial_files.go": "508a95741b8a0d1231a1d91cd32dea7f9d6e99e8e6e6f61872686df5ed661c9c", - "services/agents-api/internal/api/initial_files_test.go": "59c5430f21ea129177b14002d45dbda87c9b0c554ec048ea3d3fb3f922878875", - "services/agents-api/internal/api/inputs.go": "076ee358a220acd6f82187139909d376291073a068d93474c4d51f67720115b5", - "services/agents-api/internal/api/inputs_test.go": "46ec777b5b30111d8bd5226d6501b0a3772563ddada43e071a9c977b8c781ead", - "services/agents-api/internal/api/items.go": "eae0f884d622f7fb0ce5801d7298c093c76d8ab0a7f3c0674e1431d949b57f84", - "services/agents-api/internal/api/items_test.go": "d25fd2ee333cb36b45c7763940024b07b481468655962e680113ee5799d49525", - "services/agents-api/internal/api/json_request.go": "44945787ac6f85e1195de6d300d5688707c33ee4cf8bcea5ffc32e0594b58d12", - "services/agents-api/internal/api/mcp_configuration.go": "326c5241e1dce30e5ea64f466cc48d48f979105488bae336f1354eb4eb49b41d", - "services/agents-api/internal/api/mcp_configuration_test.go": "5b83a46013fa14225242708fd05012d94ee147ef0ed885a0a3cbbe479f83926c", - "services/agents-api/internal/api/pagination.go": "c020d83db0df2aab486186988049b4060c62639128206197f67939feb3b94af5", - "services/agents-api/internal/api/pagination_test.go": "67e347f28552f858d83ce51fef3696bec19c8838b338f1de3c8dce3330a5b24d", - "services/agents-api/internal/api/saved_configuration.go": "310c58211cc47de566a9739b2df1cbb6423c8a24e2bc43a462bd098bc02f884b", - "services/agents-api/internal/api/saved_tools.go": "c867816d38cc43f51b9a799dcfb1b32684560443c1d2fee210dc093896660b81", - "services/agents-api/internal/api/session_agent.go": "59002dfddadb8d92fe29348c67f3de1e81e22688f1d1b3ca01d9cb82e53b8072", - "services/agents-api/internal/api/session_artifacts.go": "b4b868d3bf08be51e9304b9ceb14ac6a3fce6a75bac39f121d407ad44e2157a5", - "services/agents-api/internal/api/session_artifacts_test.go": "b7399a11bd3fbcd0a2e3755044ce369ebf44554056b6a5a8b3c5256c02c8c09a", - "services/agents-api/internal/api/session_creation_identity.go": "5ed948e8329083c27a8c16218084977df8194780d12cad17cac15d29cf1b3cfa", - "services/agents-api/internal/api/session_creation_stream.go": "b98c99a27551fbd0f910516e92621f1ae9f5b8e73c6d1cb08556e738f0f2ec61", - "services/agents-api/internal/api/session_credentials.go": "9798be8e77c88fbbf85e926e0d16515cfcf200f47b4aa1c8bf7f7efb46d02ffc", - "services/agents-api/internal/api/session_credentials_test.go": "c1cb202885588b254e23da0c670c727eabe49f0e2901ff437adda5e4486170c9", - "services/agents-api/internal/api/session_deletion.go": "9f8d92edf328945e1af7dba8fb23371e4e94dd24cfaadcad0c36619117cc9f35", - "services/agents-api/internal/api/session_environment_http_test.go": "7303079074c4a8a9fc762f2fdbb89762c5ef340f34adc6b92c3f725f8c7bba5d", - "services/agents-api/internal/api/session_environment_test.go": "3d1b0589ec138b092a00a3bf5a1d633364d457a5773057fb353788b945bb7ace", - "services/agents-api/internal/api/session_initial_input.go": "c03c0bbde74511761c4cb16edd3afd20682d121405f8c2e4a91f31aea41029d1", - "services/agents-api/internal/api/session_initial_input_test.go": "131c654c5191f5439a8d0794b6066a8d7def34e7d820c97bcdaf2d9c2079c570", - "services/agents-api/internal/api/session_metadata.go": "0ed277ab051b6ef5aaa75f69e3c6e2ca777a5278978d7dffd049ac58bf6862ff", - "services/agents-api/internal/api/session_request.go": "1400f43f7548686048f911892e5c39253997a201f4cfc8c91758d78dff5e10dc", - "services/agents-api/internal/api/session_request_test.go": "3660d73298d0e30e0db7d209b1b879772ded6de73ea77886008f8007e9dc1ade", - "services/agents-api/internal/api/session_response.go": "5a4683f86c978d3ede17968637c95649816b1c55fd4dceafe4e68b3369093132", - "services/agents-api/internal/api/session_template.go": "765f306d76780621cdd3f51eff3891fe5a8d87fba7a3b4fd5832e1061fbb160d", - "services/agents-api/internal/api/session_tools.go": "583e9573f90de78f82d057e579d89c5a52d4478894bacd91d9d2e1a5d3f08b57", - "services/agents-api/internal/api/source_files.go": "2cae9b0819dc8beb98f7240880560cc8ec17c02b5638b2424fd130061c7e4fcc", - "services/agents-api/internal/api/source_files_content.go": "2dc6a35c05266d54c8711733c5f1b6469714698edfacfa3658262a1f430d33c2", - "services/agents-api/internal/api/source_files_list.go": "876a5623c24b3e7c624c0bf5fdc113df90e073a91707898e075a4ddc95baa19a", - "services/agents-api/internal/api/source_files_list_test.go": "9c88ad48d51738aebab99fc9677e5d26b98bbdad3ebf03f3f7b126ce93ed2e5b", - "services/agents-api/internal/api/source_files_test.go": "b874826d0adda2679fb8c5e51e08308ee60b195014b12b49ba4a8434ffa1b024", - "services/agents-api/internal/api/source_files_upload.go": "f05e375a42519d6516f1d4f2ffdf78054700c0f1cfbf232ac55e506c2d8ac949", - "services/agents-api/internal/api/stream.go": "78b8b13603ce7a9ef175979ae0a654daa47f55ceb8c90ad68df27ef4c4273f8f", - "services/agents-api/internal/api/stream_test.go": "ab4849b955f202e727d9d3a11b73c22014eeb64c624792854cc0120e8e6e5689", - "services/agents-api/internal/api/text_configuration.go": "4d9fc885332ba76420e1a31634ebf82a69f8548ba1ac874582ffcc98ae8d4c1c", - "services/agents-api/internal/api/text_configuration_test.go": "0221cf263a652540f66796abcd20274f714a619193d86a9b9267cf6669aebc47", - "services/agents-api/internal/api/turns.go": "aa86ce48478ace13745eda5f86f244a0dcd944398b4872b673965fdd04020029", - "services/agents-api/internal/api/turns_test.go": "953ce95d03a58f6069248fe7664296d8bf24a113e1dea6fb86bff6455a471421", - "services/agents-api/internal/api/usage.go": "cf1ae822be0bb36812aaabe543b902ed33d2bdb956b53221228f77d3a164b728", - "services/agents-api/internal/api/vault_pagination.go": "4cb21ce9afa51063967987f6280fc4c73217df9c586e5dd477f042d8285f9dd9", - "services/agents-api/internal/api/vaults.go": "c76b4842f9e3523133fb8f01a141ac6397f11887bcefd000ad64e7d36e5f445f", - "services/agents-api/internal/api/vaults_delete.go": "f26c68561bef1a206aa22d842c00d47f831836439ba1958b762d08467bad2062", - "services/agents-api/internal/api/vaults_delete_test.go": "f3df8ad536d583ad3996572f862a38799f8b1f7e3247460abac31e3f1619cfab", - "services/agents-api/internal/api/vaults_list.go": "d413bc809c2cbe506583462f1db1d3f93d53bf02588176baafd0c6d4ccd7145a", - "services/agents-api/internal/api/vaults_list_test.go": "c8a2443226ac2506df1512701a8014b0cc836ecab36f20ebcda38857b6b797f7", - "services/agents-api/internal/api/vaults_test.go": "aad51d1b4d79d1948ff43f35db5058e60bdc2877b81cda62eab1280d62e226c7", - "services/agents-api/internal/credentialcrypto/cipher.go": "2d48d7d9095e5f9f482e3352490fcaf9061e15e23907a04376faf2f23e2a6217", - "services/agents-api/internal/credentialcrypto/cipher_test.go": "4c04c7f5e872c9e6301b08676410a245c8c3da92034fdc6c2607b859b7da99ac", - "services/agents-api/internal/credentialcrypto/environment_file.go": "f8b1fed9828a267aa64aa3f049320aab86b6c5c2e30f954c0b8ea3ee4cd73033", - "services/agents-api/internal/credentialcrypto/environment_file_test.go": "8a48e7d510efc354b5d42d645ec85ee38638f3bc629c06a7a09e1a83a49de6d4", - "services/agents-api/internal/credentialcrypto/environment_setup.go": "c5bfc62bb7c79a45f96d2d2b3b075c5c398b6fc1c7e8b93de84e0cdd7b3cce48", - "services/agents-api/internal/credentialcrypto/environment_setup_test.go": "b174bf301d3c8142bca487e0e6f90cc65a3c1f499ac73fed9a19ecf8d05be57a", - "services/agents-api/internal/credentialcrypto/model_execution.go": "eb859213872d857dbe4a2b3eebefc20bb7eebc19098ad72ad60fb5954bb6822f", - "services/agents-api/internal/db/queries/agents.sql": "43b896f90c7a0cb01e1365c0122ff655d3f41e211fdc5933eb979c2e414df38e", - "services/agents-api/internal/db/queries/devices.sql": "fb3b15dc54c049e484bc1537f25963804374508a83eb15417ffee0dff93b3701", - "services/agents-api/internal/db/queries/environment_connections.sql": "dc93815e1a45ea1ed38d14c0bb64bbaeb385b19e519fbc0244ccc9e43bb02840", - "services/agents-api/internal/db/queries/environment_executor_credentials.sql": "e5f2dfefeca9306156b9ab2c004b34674bbd4bd16c82f5a724e543d7b028682b", - "services/agents-api/internal/db/queries/environment_file_writes.sql": "89179ed02ee9cc2950bea35b6852d86ee2dc86e24ebeda1760cdd94349721a10", - "services/agents-api/internal/db/queries/environment_input_activity.sql": "95899ca04519de90d41af5101abaf695c0ab443fe363d7c075bd207955979856", - "services/agents-api/internal/db/queries/environment_input_expiry.sql": "a73e0636d833c92c9dec65f574ef2e604ee672186c14c2dfc15ab3234ccad61a", - "services/agents-api/internal/db/queries/environment_inputs.sql": "ec9023f3fbc2e54eecc67e38a0efdd2e970242bd7245efdf33410819c3be49be", - "services/agents-api/internal/db/queries/environment_setup.sql": "ff5ca2fe9f5b330539630362ab6fbcdcc70d7ef3769a6fc6f315ac5493d91198", - "services/agents-api/internal/db/queries/environment_templates.sql": "f56e641bd7d5eef7356b6b2442f682a837cb193ecab644b03f72065b9a0cccfb", - "services/agents-api/internal/db/queries/environments.sql": "15f0c083a874ea86b293499316ce5f4022f0d2d346bd47c6c6c18158ed7d08a1", - "services/agents-api/internal/db/queries/functions.sql": "f7a1f8b58dea54b5e9af7feace5d93c805289b5f8612b121486fe1088b694d45", - "services/agents-api/internal/db/queries/initial_environment_files.sql": "dee74567387496b9d201d0f0c2f5c69f73bda4f9d6667813fa1591007d5168fb", - "services/agents-api/internal/db/queries/local_environment_devices.sql": "408cb633f8668cb55274113bf042c4d88870ee8610b9961210e218fc5bc70a58", - "services/agents-api/internal/db/queries/mcp_credentials.sql": "7ca1f1469ff8a6af9e9b7b8cc977e7609b0cf9ec8e0d68856374afa167e63d5a", - "services/agents-api/internal/db/queries/project_scopes.sql": "71be3fcec54ec2c0490eb85618a18bccd77b6138c3b81b84cb46eb6453cadc15", - "services/agents-api/internal/db/queries/runtime_allocations.sql": "78ea81adde8e1af0fe4abb3b67e636baded78fdd1d8978623804854f808bb0b4", - "services/agents-api/internal/db/queries/scheduling.sql": "eefb34e0452a35fa31dbb9fb758b2ed19080aab3a32e6360543049b3129bad29", - "services/agents-api/internal/db/queries/session_artifacts.sql": "c78ac3c0751ebd6d1a4a154022aea09419a561d26c491f1e46d30f728c9b55bb", - "services/agents-api/internal/db/queries/session_events.sql": "ca1b98ee2b7cc40ec7182ffd359d7f7e14e01788e5a4bd55d0b2f91deebbc9e7", - "services/agents-api/internal/db/queries/session_items.sql": "34999f34dcafebe205f8573f32738d3f0215800a1e5e89676eb7879adbfa5a1c", - "services/agents-api/internal/db/queries/session_model_execution.sql": "e81add95b12b277b94819ac9f09a68d215118d080058725d38d04a15fc2dc5c3", - "services/agents-api/internal/db/queries/sessions.sql": "2ebff618b2a9504453dc6ae8b2f9294060df6d8985f70b048892342e9d0d55e3", - "services/agents-api/internal/db/queries/source_files.sql": "e0e4286a3e23e06c869985d06134ac4db72029f5a2f031158111f3cc5731b300", - "services/agents-api/internal/db/queries/subagent_identities.sql": "0bcd0d0cf603cbb228a1e4ede60816bc6e39668a86dd9492d1ab06dd17f8999d", - "services/agents-api/internal/db/queries/token_usage.sql": "07eb4fd281783978024dddaef7f761529b7793b75f8d548ceb767cbc80743bd8", - "services/agents-api/internal/db/queries/turn_events.sql": "34be3c66558c2a10fbef661fd66bcc84ada78229667bd9d13fac4e747f107891", - "services/agents-api/internal/db/queries/turn_reads.sql": "da495fb44102cd307f08b30f122bc70f7e4e4068f69677c53cd86f3831248a99", - "services/agents-api/internal/db/queries/turns.sql": "020042445d2b6239dc520d067ff4f791e3296ebb797531b9f75d5f01b173e996", - "services/agents-api/internal/db/queries/vault_credentials.sql": "c89cee3015a6804b722745c6863b53d3a8019fa3b5293420af7e64790faac26b", - "services/agents-api/internal/db/queries/vaults.sql": "b425e6a5839aa3fe079a285899ffa2bf4ab84fc6d81fd54b2af88bd7acdf858b", - "services/agents-api/internal/db/sqlc/agents.sql.go": "ce699e5b664ab2617bcb9cca6b54fd1aaf726517220c71f80a3ca830516adff0", - "services/agents-api/internal/db/sqlc/db.go": "94ebb623500dcd4f52eec4cae2accd4eed5f35fc96f08db088611c17346fc75a", - "services/agents-api/internal/db/sqlc/devices.sql.go": "73b2241aea2443c04116eae61355c6a5d0268057ac6115f855dc11fb741ff102", - "services/agents-api/internal/db/sqlc/environment_connections.sql.go": "a8fda31c2379134efc748b336ea812980890d4d34c5050914007716a545c417a", - "services/agents-api/internal/db/sqlc/environment_executor_credentials.sql.go": "92886e4d792b0e053a277a87e631748b4be5157d9f9e3b34819dcc47e764c87f", - "services/agents-api/internal/db/sqlc/environment_file_writes.sql.go": "e44797e2211fda676c3add5aad771eb4164408c83b54c7a6f3bbfb35946ad9f2", - "services/agents-api/internal/db/sqlc/environment_input_activity.sql.go": "7445a45ae3a251094e024631a7ac2f280758c586701c9c84adbffa33290ccd28", - "services/agents-api/internal/db/sqlc/environment_input_expiry.sql.go": "33229e897b8a6327a99952121e6b9b6fef0c6f093f8bd59ecd9268eec057c29b", - "services/agents-api/internal/db/sqlc/environment_inputs.sql.go": "909b6f5ffdd510b1602267808d6e87d404b06ae9c91a9b5aacc1e24ae51d56e6", - "services/agents-api/internal/db/sqlc/environment_setup.sql.go": "1903f2528d01c8392c57689e626891e745d055504059c0dadc3dd9f8e7a0eb59", - "services/agents-api/internal/db/sqlc/environment_templates.sql.go": "9cb34fe8e1d50b7d12126b66a04b5171b5d820afb01f7cac380b4cffb1b00ca1", - "services/agents-api/internal/db/sqlc/environments.sql.go": "08e381c0b592ab042740349f455595b288a2620c87cd98bff09d9f9edd952817", - "services/agents-api/internal/db/sqlc/functions.sql.go": "c29c557fdc04d46d8234bc96ad8e6d2c106ccec93302191a92bab60e9ec1de55", - "services/agents-api/internal/db/sqlc/initial_environment_files.sql.go": "24027f0c7958911fe31e81ff052decbe6052f18c22c44561e6203eb0de7fcf74", - "services/agents-api/internal/db/sqlc/local_environment_devices.sql.go": "05b3db8cc439e0cff063901516c8352529fbee2be6cd05bb7060e3e249a02133", - "services/agents-api/internal/db/sqlc/mcp_credentials.sql.go": "4d3818a74ddf4749c7bedc80df217bca4bd55f0a4992976922facde5074e2a6e", - "services/agents-api/internal/db/sqlc/models.go": "c0595f4a8767d4e923244b7e33c701f43f325730a1c21cb9c4d69d0e696f40bf", - "services/agents-api/internal/db/sqlc/project_scopes.sql.go": "2de28fa355e1f23aba693a63fc9ccce80ff132d03a178c010cef0945bbe9acd5", - "services/agents-api/internal/db/sqlc/runtime_allocations.sql.go": "4ae0c4f15d851bd4f63e7f8f1df59e31a3556376ac4e6881d4bac486fb8c5b44", - "services/agents-api/internal/db/sqlc/scheduling.sql.go": "d169f5a3c1e77409e539606e97df0dfe83ce71f4d60a80395bacb2ad94a1485a", - "services/agents-api/internal/db/sqlc/session_artifacts.sql.go": "99040318b7385c68e612970026f6cbd5e91967f842670207920a967805c4cc2d", - "services/agents-api/internal/db/sqlc/session_events.sql.go": "774dde336a8692d1c24e1c740d4c125210932fb80af94df184eb664b4dcc04d5", - "services/agents-api/internal/db/sqlc/session_items.sql.go": "4754ed4e8e123a601b0838c2bbde770f779607ab5bd7f152ba5165b0fe40a67c", - "services/agents-api/internal/db/sqlc/session_model_execution.sql.go": "eed84911f21dc2425218919f89dbf5b2fc1805d3b56881ffe2886257209e72ab", - "services/agents-api/internal/db/sqlc/sessions.sql.go": "b1eacd1eaa31888b42f1552f598a8a4503f5980a085e1dd801e3c2d96112b887", - "services/agents-api/internal/db/sqlc/source_files.sql.go": "ec7e25c3930ad81304bc24817d53f8b359f776cafdfcbe85e00ad9ab36bb46b5", - "services/agents-api/internal/db/sqlc/subagent_identities.sql.go": "4e7bfdb223fd0779ba58a858181de319cf32b0747dc25df7d942808fe14d1427", - "services/agents-api/internal/db/sqlc/token_usage.sql.go": "feec479ca9cad0846d8e19f148284ca66301264242c02cbc53be9685da55e055", - "services/agents-api/internal/db/sqlc/turn_events.sql.go": "838e41551352c91f14a9d9dcef5556d38bddbe26b63c5541223c78540778c854", - "services/agents-api/internal/db/sqlc/turn_reads.sql.go": "25107480ad03d6f7f226b3f9fd10b11ee595a0bfd3c7ce9b2757bb76b76b8b6c", - "services/agents-api/internal/db/sqlc/turns.sql.go": "4d6e9dc115b91e1c764a836766a83ce0a997578bc13a192b55d04067c809d576", - "services/agents-api/internal/db/sqlc/vault_credentials.sql.go": "77d4df9d5d7af68f7ca35da1f27d5c255fe1270e0353df71b6e906b0290673a3", - "services/agents-api/internal/db/sqlc/vaults.sql.go": "46458380bfae212474f85b17d04fb651c62db6eca134f7f716b38fb099fd43d5", - "services/agents-api/internal/engine/claude.go": "a41e4944932edef38be20e91baec2b872bc4fe67b359eb11602bfdf5204b9b62", - "services/agents-api/internal/engine/claude_mcp.go": "864ea8512909b0847c6b9a5ee72fb3a623175c5c16711c097d4f8dd181371e86", - "services/agents-api/internal/engine/codex.go": "b933230c2eed0fc34d611ae8b26d8b469b92bc5781b49fa4d6441ac380c2c8be", - "services/agents-api/internal/engine/mcode.go": "f1842e47f5f9452a5060947bfc46ec350751334c354d90537d4b0975de8c03d4", - "services/agents-api/internal/engine/profile.go": "7413b845495944092d1a9693607f8ad7e9c5a2559aae0eed01ec3e7acc8dc697", - "services/agents-api/internal/engine/profile_test.go": "660caaaeb7a90119d26539467aa73f436ddfecd7efee12ddb36a95a3de90b154", - "services/agents-api/internal/execution/artifacts.go": "800877470a608eb554f9c3d6f0851c7c831b149291e7f9bc9ae9e041203e834c", - "services/agents-api/internal/execution/delivery.go": "ee370595d2a2be3ac4ac35e4b057d3ad22dabbec1df93ee4489c364bd67adf7a", - "services/agents-api/internal/execution/directory_preparation.go": "f8e66e5c1b710cc9d6171c2ce98d85950204d6fd7975da77d7ae4455055ad477", - "services/agents-api/internal/execution/directory_preparation_test.go": "139d818f0a94d1281fca1834620cb6c1f1551f0636b5cc355410ca9ea1f613b9", - "services/agents-api/internal/execution/dispatcher.go": "f5a3f6d62f1a0d8a9c723dcdd504d5d221a245c65980722f541e2f982ebc4e64", - "services/agents-api/internal/execution/engine_profile.go": "798f31df914d11337393d5c0fb255949634c0fa7fad2f32a703e903fae8fb9ae", - "services/agents-api/internal/execution/engine_profile_test.go": "052def67d6a89cabebf6387005988a2f7479dce83efc5c8059a7776f41c4fb02", - "services/agents-api/internal/execution/environment.go": "a611c3261b5abcddcb07dbe7723f03fd7380bbefc93bb070fa3a1b27e4a1352e", - "services/agents-api/internal/execution/environment_admission.go": "41478c2ee59877ba2ed0f1126b1fcea8c28262fb594e7829fefab56812b5bd32", - "services/agents-api/internal/execution/environment_connections.go": "c8f1c1ce100b4042e1339088363204ba30236b60d24609821415848bc7c3f64b", - "services/agents-api/internal/execution/environment_directory.go": "61f34d658e754b7ba7ea72db8a1a048c1f702245747946909bab4ffc9287f548", - "services/agents-api/internal/execution/environment_file_write.go": "addea97d1071210c004b0d8a6cdf66610f11fb838dff8a7db5708bb2a707c84d", - "services/agents-api/internal/execution/environment_placement.go": "1259e5ffd2572af381ddca3c121f23cfb2ad79c34248fae7797a0eea8c160d01", - "services/agents-api/internal/execution/environment_placement_test.go": "c514c91a9ed44289b14fdb53881ad354eb532298e5a7f51cb2c2dfe978885a05", - "services/agents-api/internal/execution/environment_test.go": "953fc693311807aeda46191f6ec423c7e75c41399af7b25aca870f7db56a366f", - "services/agents-api/internal/execution/finish.go": "6937ecb906fc6f9955c564678c94505aa74d9b8e42da6903e6939811e5a57248", - "services/agents-api/internal/execution/functions.go": "d2d171ae34686f0ba9bb062dc6f67aea61666c7cb5c030455ddd2eebe0aa871e", - "services/agents-api/internal/execution/functions_test.go": "b52c7eebc71531b428ff0e4edbeefc288a8267ba23fdf01d0ab603b03c18b5aa", - "services/agents-api/internal/execution/input_text.go": "e2626c60f6b172e7ee9bd3769a6ba37d856c8bf354ec973caa6fc12bbb4dda33", - "services/agents-api/internal/execution/journal.go": "ed94df2807c24c46fa3c76d444e74eeaed0c5a1b079eb989421a1c67b402a194", - "services/agents-api/internal/execution/journal_test.go": "aaaefe27272774747a39592d54c8f0ca7e82189303a86a69580fd2faac4c2ffb", - "services/agents-api/internal/execution/mcode_profile_test.go": "1be2cc70e2dccaa5d468e99965da774b2d23987012d0e1d770e8a992743b2dd3", - "services/agents-api/internal/execution/mcp.go": "6fc4e451c6f0f61c52d3169e0a47524dab1faa66ed6d207759b3b769e1b89baa", - "services/agents-api/internal/execution/mcp_credentials.go": "438a34caeed81e84cde51d0c2e9a0aa18642e6d41305edca854ae41db0f62f57", - "services/agents-api/internal/execution/mcp_credentials_test.go": "47dfa40e4636d81cd215bd4f7bf552e18b5ffd83a3ab47a127813b44577009c9", - "services/agents-api/internal/execution/mcp_support.go": "1b97f8f9d04213915d6602790b32a7753df9e4ee43d874b261450d928ea4c4b3", - "services/agents-api/internal/execution/mcp_support_test.go": "c6fd993a39f9e507631ffcef37718fc368ba2505316746ce61b0cc1bc8c8bc8d", - "services/agents-api/internal/execution/mcp_test.go": "1265f4a9c4988347aaacc5c8632ee7bf47bd284ae11a354db70fdb8a9332cf9d", - "services/agents-api/internal/execution/model_execution.go": "d62463be7854d3afac750f181ed1ed7ba7b7e33b493c6964c5c346b0bf9c324a", - "services/agents-api/internal/execution/model_execution_test.go": "00bae29f8a0999ca9348cdafe7c7dcb17121b01fff7e8c8a658662d3b1ed6376", - "services/agents-api/internal/execution/policy.go": "91131c31f3d79d4dd7eb6a0317d66c58a652b6f0a2a560ce1cc434da0f6a91aa", - "services/agents-api/internal/execution/preparation.go": "803055a8b9ca7f4e53f2a681f8e3f706d69e0c6290c06653570a282dd43fc9a0", - "services/agents-api/internal/execution/prepared_dispatch.go": "6d8085ff105e5c00ee185ee7b1fe705b814c2ce56a5cd0a5275d59d54ca36f0c", - "services/agents-api/internal/execution/recovery_test.go": "5e1a40c4527529dba7b354be781cd8de1ca6102bb34b8438481af92b45459d54", - "services/agents-api/internal/execution/request.go": "31b7771faf5b28b153a7896ae015feb9932b2d4dab52b8dc183e96e993151547", - "services/agents-api/internal/execution/runtime_connections.go": "27a00cc04fddcb6321b2a8bd38343920ae1d4de3f53239fc7bca198dde2322a8", - "services/agents-api/internal/execution/runtime_initialization.go": "43a81e930428497a2cdd2e97460f297586f429a62a4b7036c6f1f1bcebe3c358", - "services/agents-api/internal/execution/runtime_initialization_real_test.go": "710109ff148d4b8147d4ffe95e88f514cee23b479f5317357f060b9d9826efcc", - "services/agents-api/internal/execution/runtime_lifecycle.go": "153d3ee2a793e2bd87f9051c6075df891cd6c52eed6e53713f489fc6f78de74d", - "services/agents-api/internal/execution/runtime_pending.go": "af56161addac7addedd5dec13e3145e28c037be54bd40baec7fa4178617a0ce2", - "services/agents-api/internal/execution/runtime_provider_selection.go": "d80b03f739dccc18598f3806bbe2031600199da5d3f23f69fe373add08b65c3a", - "services/agents-api/internal/execution/runtime_provider_selection_test.go": "3f2966f2e0e80e2fe5c355cc05bb609ae35a94c505b8192306325213b5d0caa0", - "services/agents-api/internal/execution/runtime_setup.go": "14750ea7db7a7972e9b818ff2d258c47ef0a5cef75f0bf60454134a216bcae8b", - "services/agents-api/internal/execution/support.go": "3dc2231e3f0ee4e065e8f4facc37c5f32696cf54f67087dfbcb167eff1270a10", - "services/agents-api/internal/execution/worker.go": "a0d2ae008936c409fe1091bedfb53627c49aa9d961c4514365b4b9aaac3f7c9f", - "services/agents-api/internal/execution/worker_device.go": "d6e079f32471a4aba1f1862a9fb6b0ccb54bc0a2a2af61f1d40d6f78a52b9df3", - "services/agents-api/internal/execution/worker_schedule.go": "290715115c03804b61f33a87925533684edc85bb4c8ec3625545da7a6ee73850", - "services/agents-api/internal/executor/codex/config.go": "cdb0ceb4fc6d779bbbf51b542ef1d78fb5b37a35c8aed5fbd992816596b29e47", - "services/agents-api/internal/executor/codex/credentials.go": "61b39c3aa88267094a1b72e289e5cca491087f674205a0b87d435e4c221e52fc", - "services/agents-api/internal/executor/codex/credentials_test.go": "6d60ba0cf5ef2af065f1640c5cd94bcb0e65b8b5e560e071db707253b5cbefe5", - "services/agents-api/internal/executor/codex/harness.go": "624fe6ec2d322443955b30658f7ef7dd28cb79c194998509c472bb4c2f4bf78a", - "services/agents-api/internal/executor/codex/harness_credentials.go": "85186e818b322996c775d660398de2c1501f6eacf0734a803fd3a7a7b7a34d46", - "services/agents-api/internal/executor/codex/harness_credentials_test.go": "f6eede3a00578d7ffb50d0af12f876bb07ba9993e5bce5514b3170602e9cc042", - "services/agents-api/internal/executor/codex/harness_test.go": "1487d7ed23ea550adfb1374e2d7852768e311ecb908f318ec4d7b1c0027b7678", - "services/agents-api/internal/executor/codex/lifecycle.go": "0343e126a9e7be5ac36fb8804c4b2f929345c182e5a4424ea227be10a4758111", - "services/agents-api/internal/executor/codex/lifecycle_test.go": "83383686eec008afd4da111066fb6532a11d34a1e0b91949990e72ea9a5f7208", - "services/agents-api/internal/executor/codex/messages.go": "2da0dc76e17e14b8b819456454a4efd3b752b431407022bc72eb8cf02bbc0b13", - "services/agents-api/internal/executor/codex/registry.go": "6e4217af880b3c588c96919af9ffcb30e5e392d7e01466b29dcd33288bdd618f", - "services/agents-api/internal/executor/codex/registry_test.go": "18edf8a21585f8eeef0c65f4690304708759198f342a6d6bd9b88fa922d1b385", - "services/agents-api/internal/executor/codex/socket.go": "1b46de6f106242c8f510dcdec5f6be60245451dc1d0c5c2d12c49a7e1ecf0159", - "services/agents-api/internal/identity/principal.go": "148a9cd43c761ac6b85f274ff21bb3339a6d34b863190c5934985f5673e06b4e", - "services/agents-api/internal/identity/subject.go": "70fcafc803f4f4dc9e567a994cf192b3a91578796498b7662b68a867339b9cde", - "services/agents-api/internal/items/command_output.go": "6b486bc923c283be8ef2a35d4faed891918e9b6749e4af7671e093a6fea8b2e0", - "services/agents-api/internal/items/command_output_test.go": "d76febe3c57ff418be336c35e4725a1fe2044078244383c89b0b7650f4d834b5", - "services/agents-api/internal/items/inputs.go": "bc4607fc3f1e2aa311b8369bd2fe320c98fae7a2945c5a32f70d901e163074c2", - "services/agents-api/internal/items/messages.go": "5f560a7c680033ef62eefb77c1ce8f7341dd045de71f5f671d01fd6d50e24901", - "services/agents-api/internal/items/messages_test.go": "618cd803609b7148275a8ceb2866cd0523e935ec8b3eb275e68d80320d4b1466", - "services/agents-api/internal/items/tools.go": "cf43b69e07d4ab4b49878f8102aefc1d4331b8d77af3d2b97140568a573e17c0", - "services/agents-api/internal/items/tools_test.go": "a44505cdcb05223aa5495fc94855558aaf2c42a6400b16beeea6e69fcd0a5582", - "services/agents-api/internal/runtime/gateway.go": "996baa23672065f496810079ad1991904c80fd6608565ff5ac652df0402ca0f0", - "services/agents-api/internal/sandbox/docker/bootstrap.go": "08911b80b9e05c4bd5fa275c17cd8e9e54d075c5046075ee2f2127c5932fcd66", - "services/agents-api/internal/sandbox/docker/command.go": "30e598f513cfae59c2dd6d13e5c0905b19337ca5c45f9c420c9665498f29cc02", - "services/agents-api/internal/sandbox/docker/provider.go": "1eff16518452d76facd38d5a4f4700edd90e8ac4f298f2498b6573761a36dc3b", - "services/agents-api/internal/sandbox/docker/provider_test.go": "a99064228eed615f5a426ceafde588954aa092d5586666a3c0b9042a0df989ff", - "services/agents-api/internal/sandbox/docker/recovery_test.go": "612570539134b59aac7751ad352a0588cb81eed7aba56498003409750e8f1b96", - "services/agents-api/internal/sandbox/e2b/bootstrap.go": "07513161c7a4d177df0eac7aefbede19c2a7793febda601fbd37ce41afe29b62", - "services/agents-api/internal/sandbox/e2b/command.go": "72961826953002c80095be35787c9bb2533c867648b69783d51aef429523fe06", - "services/agents-api/internal/sandbox/e2b/command_input.go": "ce1baebab027e601ac4a5bc36efa115609930ef25289531df0fc5dab78af0d6e", - "services/agents-api/internal/sandbox/e2b/command_input_test.go": "684aa2caeb5f3bb41375758aec58ba2fd093c8b9b80c2a04d8d06fce6b228ed2", - "services/agents-api/internal/sandbox/e2b/control.go": "381b63f8242a5d76b413827f80d5140b8c31fc0f05756dc3bd821276f68f5621", - "services/agents-api/internal/sandbox/e2b/envdprocess/LICENSE": "b4ef1bf811cb4095229fb86b574199e467c78f0ac4078cf8be62189e1fbd0818", - "services/agents-api/internal/sandbox/e2b/envdprocess/README.md": "366515f17e0c3ecb6f0a8837aee5cecff1cf49f4a136a2868f91d59cfaf17c46", - "services/agents-api/internal/sandbox/e2b/envdprocess/process.pb.go": "e13af3ccd50582e3c8cec9c07efa27269a5140a6118554c3531671bfce03f106", - "services/agents-api/internal/sandbox/e2b/envdprocess/process.proto": "8edd9358c7dbfcad96796b3f0ed8d14c262b8b14d6bc7d5e84d468941511b8e0", - "services/agents-api/internal/sandbox/e2b/provider.go": "6cdb1f26e2d5b00edc1bc89993a635734e1c576de7c97cd6fd2f6cb4ebda5557", - "services/agents-api/internal/sandbox/e2b/provider_real_test.go": "b5ec18c9b05435ad966efea0609d141f84eb9a2e4cd584f21db3015d6293504e", - "services/agents-api/internal/sandbox/provider.go": "79a3078aa995f68af0d38a86032e80b2770f642c5e047a9087b68b11e8a828a8", - "services/agents-api/internal/store/agents.go": "58e36c252a5c0033d54155c7a9358c476fd8fe4e4323bbe301b4399f743ce469", - "services/agents-api/internal/store/agents_delete.go": "044f77cc4589d1cf4c6749c93806edb70c3f5648603b75dee1855c4e1a882c65", - "services/agents-api/internal/store/agents_delete_public_test.go": "e756a9b5239e3e5e6d296391623856a9d4d326982ce1d6ace5ae1ee90ed9a26e", - "services/agents-api/internal/store/agents_list.go": "c6ae4ade605affb15c79b73a84e1af2c04d244266b304298872763628c3901a3", - "services/agents-api/internal/store/agents_list_test.go": "9100b0d8387712aae8817e00b7c00db7e61feec370807a9824d2aa81a9ae6728", - "services/agents-api/internal/store/agents_test.go": "7376fcba5dab13a87ef8028947e97fa3d7f3ca6f5f0764fea3daa48b23bb8f08", - "services/agents-api/internal/store/agents_update.go": "83bbfdf4287d96650f754f0b697e9fe34843588915d921642a77600a7443c07c", - "services/agents-api/internal/store/agents_update_public_test.go": "1829cf7ca95a33a89a4a81f9b4516188c494fa0cf1b63254e62290af217c1dad", - "services/agents-api/internal/store/agents_update_test.go": "909d8046f3f3a82d374ead37c1f8586d7c231a0d2ed3a9aab9f02735a888f7b8", - "services/agents-api/internal/store/artifact_capture.go": "20b0837cda58c1a4d234a6a6ee5d87931fb39d07fce7858e4937011070cd4f4b", - "services/agents-api/internal/store/artifact_lifecycle.go": "7cffcaf7c6653f22576368b47f65682f8caf18c269247742920c884b4621d49f", - "services/agents-api/internal/store/claude_execution_test.go": "7073fd1234ee69ee7a827c7f05a293c091e5b5929e73e239e1065d096329d042", - "services/agents-api/internal/store/claude_mcp_test.go": "ea264481e669db541a2eda836c44246706a2b84a34e7fecf64fcaecfe2d3e3d1", - "services/agents-api/internal/store/command_output_test.go": "83e153ffc2a6a1bfcac53dd9514d0f9d575f5146773ad877cec3be679386af9d", - "services/agents-api/internal/store/credential_status_migration_test.go": "7c2430fe4b8fcab8645122f688b164864059c2b7d265d1a76055cec58c198dd1", - "services/agents-api/internal/store/devices.go": "7e8a1dffb8c7df5c880b1db0023ceddd1d2bdc858fba9a867dac968ffa83b501", - "services/agents-api/internal/store/devices_test.go": "355c18153a8587b728082d232a2cc17e31baa903b4aa31f6f186819be978d621", - "services/agents-api/internal/store/dispatch_test.go": "b1a2969bd1bfe1ec84c9d7d367690995ea6224563af8e6a395a673e15f25b27c", - "services/agents-api/internal/store/environment_admission_test.go": "0288525becf8b7fd13eda7371b8e619279dd1885352be70ce3a2ebfa2f3b8af5", - "services/agents-api/internal/store/environment_claim_worker_test.go": "4fa4de45d1849cbd5991bc4503cb2d53e57f57237d6378fefeb216f1fbeb4bb5", - "services/agents-api/internal/store/environment_connection_events_test.go": "e64b06410d547f25dcd4909354d8ad9bc43f1f94c3541a68e009172e01f7f01e", - "services/agents-api/internal/store/environment_connection_migration_test.go": "c5ac8db09355593875a4a324b1601880292640c73da2819af044f6b9dfb9eca9", - "services/agents-api/internal/store/environment_connection_recovery.go": "8a9b962d42528adeed7166871feb317b07d5647a2bd127e547179b3f0461bc75", - "services/agents-api/internal/store/environment_connection_recovery_test.go": "aaf5dcc446b7978fa0b63c719ce868d13f35df8f59f848dde01428b2958953a3", - "services/agents-api/internal/store/environment_connection_worker_test.go": "05750ef94f5bcfdb29a9e1f990b34b0999ed1684daa1a421d6b6708e96a8e15c", - "services/agents-api/internal/store/environment_connections.go": "835ff596bc26814ed6a0ecc1dfc3d5f07d9be3f3ac60a14436d2b492d70df487", - "services/agents-api/internal/store/environment_connections_test.go": "096c764d56b6290b533ee60e288b6266b589eab64b04883d671a78a98b80c826", - "services/agents-api/internal/store/environment_device_test.go": "bcd284fa768aa7299ce119a5e82d806721b9c3f27ef81bca2c8713fce379e242", - "services/agents-api/internal/store/environment_directory_active_test.go": "a9f43eea9f6a75c0888fbc70e03292aa1c0ff6647dd89475ce2f9857f02e18be", - "services/agents-api/internal/store/environment_directory_native_test.go": "8f1d5501d4a4a451f79623fe12d70a738b0ab2c80c0528cf3cad0962d3e15824", - "services/agents-api/internal/store/environment_directory_test.go": "d9081e97bf7703d9ca81672fb9ee2de0ba815e9cc51cca06295f6c28d6143646", - "services/agents-api/internal/store/environment_executor_command_test.go": "ff956cd812fd0ce30633306aa909cdabc2569e5a0e2ec9acccec26caaeaf57da", - "services/agents-api/internal/store/environment_executor_credentials.go": "5c73d3e488bd6702612478b8cc5eab1bc56bdce9ce0b711dd8ff6d052ced55ec", - "services/agents-api/internal/store/environment_executor_credentials_test.go": "dea5e4d1697a4828b40fa5273e8f3c5a8edf2390a3b72a67f62fd60ef4ec1c61", - "services/agents-api/internal/store/environment_expiry_dispatch_test.go": "735bfa35dd7a7cf5e81a0afdb4b64c2bacde457d2d46146da15744dc7abbc65e", - "services/agents-api/internal/store/environment_expiry_worker_test.go": "ee559d9afe2c8e122256383edb108f55205cc260d34cd3623bc76efc11976abb", - "services/agents-api/internal/store/environment_file_write_migration_test.go": "c6a6168051263a05ad26ae39c26de1777894a52fba110de23acaf53b9da78f19", - "services/agents-api/internal/store/environment_file_writes.go": "83ee35002dad59e411ee8eff150ce6d9a8a95d27470b078c0afb44889657e425", - "services/agents-api/internal/store/environment_file_writes_test.go": "10266b68d00005b68c14f78a9b7104cbeaeb4ef5b5c50067614af33676b0fa72", - "services/agents-api/internal/store/environment_files_native_test.go": "1be3a8beebdcf9c3a3384faed7522298b7ac24f3e2e8eac4474590cb021a2221", - "services/agents-api/internal/store/environment_initial_input_test.go": "a10bb3f934f400cb89dad58acbce18e97a882ac11b0b0a169d4d8825a28dfcf1", - "services/agents-api/internal/store/environment_initial_migration_test.go": "541b4a52e269f48b7ed51aef75cd4edfbbcb2a9b089e4d105adeb739d12ba111", - "services/agents-api/internal/store/environment_initial_public_test.go": "ba2a354f0483e46b42f25e48ac77b6ded3dbbb9ea685ba5f616947d5b90d645d", - "services/agents-api/internal/store/environment_input_activity.go": "5b79daa5146e3887cb52796055c66a5196e4e0d2c9dac313d53b8b316ca45dd2", - "services/agents-api/internal/store/environment_input_activity_test.go": "a84e24b25bac3a72eac0cdf59a7b05dbe71da4361c3866edb73fa2fb88159c08", - "services/agents-api/internal/store/environment_input_claim_test.go": "d74f09f32969412669d7e40b560ff58fe149a5d05a2386184e8186121957e3bd", - "services/agents-api/internal/store/environment_input_expiry.go": "d888725822e7392da1661421fdf3887be72f8d78aeec5baab9253065fac43f0b", - "services/agents-api/internal/store/environment_input_expiry_test.go": "922739b912f7b5ec23371e040d54f92fb0ba828a041f9f9b03c022c5ae2d8d64", - "services/agents-api/internal/store/environment_input_migration_test.go": "091c137e58c0e775a3d8ea51170fa99b9fe21154198cac4cc902f9e0def6b0b0", - "services/agents-api/internal/store/environment_input_settlement_test.go": "aee92da5d2adca7d0cd63a9fd2fd03efa90aadae6cfee0cf3a63d11ef0d81bd2", - "services/agents-api/internal/store/environment_inputs.go": "bea504ea56b7b7ca8513ab6ed2c9ab715c378ea2cac06356136200e22e07de8c", - "services/agents-api/internal/store/environment_inputs_test.go": "cec3f2da337fbc5507b1b5cb8e6c6284ae6ed110eac2a65e484c90ed58ff39fe", - "services/agents-api/internal/store/environment_retrieve_public_test.go": "5ee92871b037ea197848fcb431e8457be2ecedc7347d6774c1441a70edec3a76", - "services/agents-api/internal/store/environment_setup.go": "65a08929dbc3d1b16127c3439dd41e01e76ed3fe6326bf15eaba776580d8c5e4", - "services/agents-api/internal/store/environment_setup_test.go": "bcc0b3065ba4f16749bd689f7baf27d8c3a41a8ac6401c347e61a64316d67525", - "services/agents-api/internal/store/environment_skills.go": "7d332137504ff515643bb1f82d337224eca95b63056e2a740bf7d338e59ed426", - "services/agents-api/internal/store/environment_skills_test.go": "2807ce716e3a25e520875cb191f2f7452a6769030ec532c89ac1bf19b88fcd88", - "services/agents-api/internal/store/environment_steering_order_test.go": "d2f7fc48e715fbbfa912bb016ef863ed9844ef2b852eea9a2e5474f48f286215", - "services/agents-api/internal/store/environment_templates.go": "924f41920f8f4844a03cea889ab68dff6d32cd769cb720133a42db2e717bc17f", - "services/agents-api/internal/store/environment_templates_test.go": "a6abf9ba7303ea41af86148b3a611ec0313158b3a2855473a0bccaba732f52f2", - "services/agents-api/internal/store/environment_work_test.go": "892b8ce4232026ccc9a013b404178b8b99a3d737b2607c96baf0b5f4334264d5", - "services/agents-api/internal/store/environment_worker_helpers_test.go": "f1f79d963d5c54f8639237da991a1e82334637367813649d52f9696f4ccaef7f", - "services/agents-api/internal/store/environment_worker_test.go": "877de3d7d2075cf72d31f274dd33a3a23185e08dde39984ba3b904fdc9806e60", - "services/agents-api/internal/store/environments.go": "84a402a04940c3fb7c615f515db13120e96685aade4dc708b99f8dc591616f24", - "services/agents-api/internal/store/environments_migration_test.go": "35bb1e1451ed1ec4e5ec621cf444fd13e640f10c8fe832cd41c26704576f3b5a", - "services/agents-api/internal/store/environments_test.go": "99a9e0f3f86f8d73d4aea51cd069d0f14ea0bf5b292345e54411198b810ae5e7", - "services/agents-api/internal/store/execution_events_test.go": "efb016abfb69fe16922a26a2eaee7d40442daad73f55e7e6de5e98710fca6539", - "services/agents-api/internal/store/execution_lease.go": "b5d0a0f7a2e292f525c95ef7ca1d9055e826ad38b688c4daf3b08d223c7c6bd4", - "services/agents-api/internal/store/execution_lease_cleanup_test.go": "2823da44f582662f1114f330dd6dc7d9d7614485a9cdeb9c7dbf83258154980b", - "services/agents-api/internal/store/execution_lease_test.go": "d6094359f8549466bbdd4672605421cdbe15aa861402f0d99193060b3ed274e2", - "services/agents-api/internal/store/execution_messages_test.go": "15a2da3767b385038d6f2a51de074fbbff836ad2bb4919781e83cc8d6b9db065", - "services/agents-api/internal/store/execution_tools_test.go": "f94cf3a42328fad863a0e5b43db355752534957564cf1290e976d219aa4c8a4a", - "services/agents-api/internal/store/executor_credential_target.go": "fac78671c682a4b3b73fb7eec6bc1b28a720a94aff1d0371c2f0dc6d851970c9", - "services/agents-api/internal/store/executor_launcher_helpers_test.go": "d9da6f1b11ee1d71d11901db8e54674a0710986c8e5331fe45324fcd1a32754c", - "services/agents-api/internal/store/executor_launcher_test.go": "4e6f892b147e44d103acb4935118eca00f9275993e87712fe46ed79eb7c9009e", - "services/agents-api/internal/store/executor_principals_migration_test.go": "0dc60d292121adab55b4389f0a7d5383d5c32dc87d592eea1c46d5490bcc5126", - "services/agents-api/internal/store/executor_principals_test.go": "d36e7235f497f41e1bf49f3d3ddbfa098d3cc4a5d829532aed981635468df1a0", - "services/agents-api/internal/store/executor_registration_public_test.go": "fa11c125053ae69022c303aa1639878edd076628a17a09cef52a0c182936a5b2", - "services/agents-api/internal/store/export_test.go": "ab37683860992cb44adbef5c64f3ffd5765b104de0af742a665632d6d40c06c4", - "services/agents-api/internal/store/function_calls.go": "e1a1d6be5add7f4166f3e551b34554251c4117172c5690445301c59bc8a1cd2c", - "services/agents-api/internal/store/function_calls_test.go": "272a63cb40fc94fe13e2ba61ebc48b8bf94aa8ebe95de3399eb2057616783c90", - "services/agents-api/internal/store/function_execution_native_test.go": "f656b41c5762b60aa18b0ba98d88f900be3601f6d37148933d9fad05f6c2efcb", - "services/agents-api/internal/store/function_execution_test.go": "5e4decde549643649281c5d22ebb224787eabfa90e4aef26e496359cbd51992f", - "services/agents-api/internal/store/function_input_execution_test.go": "d24ece315d0bb38b2b69e39a302efbd6cb390a8aa2ee74c2f3c2652535c33017", - "services/agents-api/internal/store/function_inputs.go": "c474ac4a78ee687d67e178a5c9a3b18cbdc33c01052999053b62866591b44567", - "services/agents-api/internal/store/function_inputs_public_test.go": "47aa84e005550986474aea219850b79330ad1e2328c6ea72307a89b616e0a88d", - "services/agents-api/internal/store/function_inputs_test.go": "180291b85559f3967d415cca0134956762663044bdc6262a3ca130300b5699d8", - "services/agents-api/internal/store/function_item_events_test.go": "5d33afd89a8cd59e70e8f7848f61d338da2c58bbc664f435a5870806791b07e4", - "services/agents-api/internal/store/function_model_test.go": "e3c2543092058f48e05e218cb7da849beb6c408f124ceffc95b2255b634c0deb", - "services/agents-api/internal/store/function_public_native_test.go": "cd7fb81b976aeab14b12d3267ff56ae4fc850263944f0d8c503b534a41a1819d", - "services/agents-api/internal/store/function_results.go": "3a6e936df98bd4f36436ba4ba2e30cf22afc5e357819305160e70a16e10a9cf8", - "services/agents-api/internal/store/function_state.go": "fb09bd848657b5708fcbd34f2a383740f30322c65b92c758381d45cc2481947e", - "services/agents-api/internal/store/function_state_public_test.go": "3db99359d1de8fc62c537c66c2a3bf032e319e9127397edf3e05dd9ffaf6a52a", - "services/agents-api/internal/store/function_state_test.go": "c95d9792bb209b2f151d3fc7a95d1549aaaa8b3b7b1b76cd9a9bd9cc2e1b5e83", - "services/agents-api/internal/store/function_stream_native_test.go": "eaa45d7573163fc1aa2ae89a670cf5a1308dbfbe3753bb1cfb80fe37f063c128", - "services/agents-api/internal/store/function_worker_test.go": "5ba5fc4fba4a9eee5719276dd0200516d36304d24dd00f0ae6137001442b6d37", - "services/agents-api/internal/store/harness_authorization_test.go": "5728e3a0509bcb1d460387cf232e5a87a72c1099f0cbe17e9183474da73d86fc", - "services/agents-api/internal/store/harness_onboarding_test.go": "33384f3c95456dc1d78125264a78687b5c66102e15ba48a5cf183de8a04a7feb", - "services/agents-api/internal/store/initial_files.go": "f9784a12072013ea7f0234394f932134afcf827d7fb283f7d6858d450c0a85ef", - "services/agents-api/internal/store/initial_files_http_test.go": "c8696f63a30dc0cda2e2ef42b506d7a0eaae959313d014334533aea4dfaf50b6", - "services/agents-api/internal/store/initial_files_test.go": "e65fea1fb9ecb460c785e0a1d0aea8b4abdb1ba5f6db2e7bdc2ca49e757d8ca4", - "services/agents-api/internal/store/input_batches_test.go": "75b824cc46c08efa8c640cc437175088054ca133c32168e7abd7c38dd2eb1e61", - "services/agents-api/internal/store/item_events.go": "3dd8bb40213bb0dcd3ff7a0eb41843ea0bd36140df8ed722d31958a5667f1ef1", - "services/agents-api/internal/store/item_order_migration_test.go": "055d985b5286dd1ecfc8e5ef90e93142adb79f77bfeb516b07083fdf48e5b366", - "services/agents-api/internal/store/item_order_test.go": "c87c538d71a1083590a08320d873759748cb56f28286934f86ca91ddd55b2883", - "services/agents-api/internal/store/item_projection.go": "85664119969e4f76d37b693936fcf4ee4ea7e3a48895bc76b686caea74151550", - "services/agents-api/internal/store/item_reads.go": "1690ee48e353cc71379e0a47db6aefff45902df3252a4027bee083842b23b80e", - "services/agents-api/internal/store/item_reads_test.go": "ea5dbdfc1a0fbfb5688a2e9a6dfa0b38316764012389101d384824c8fa954cf0", - "services/agents-api/internal/store/json_object.go": "54fd5992723d9962681ebdfc444763d4b93ab9fdf64db8ced5b1ecfb2b757f62", - "services/agents-api/internal/store/local_artifact_export_test.go": "0f8d0cea710215b3e479086e7d8dcabfb2e38e38fc35c912944d4c17966255dd", - "services/agents-api/internal/store/local_environment_devices.go": "4c35e1d80e5763a9bcf447d7622f5146f02759e12928e27746e298b9f82ecf43", - "services/agents-api/internal/store/local_environment_devices_test.go": "909fc0364683a7d5a95a348934ce0c8f532338a8a6b80c31ec6648495e180516", - "services/agents-api/internal/store/local_environment_file_write_test.go": "fbb47f76df8d0b33235f909d43c8fe779d36f5b8e6f859d82f983b1461252c3a", - "services/agents-api/internal/store/local_environment_worker_test.go": "275345b414b7d4a63085799f1c77335ff0097cc86c71e6a14b4f3b148d5826b4", - "services/agents-api/internal/store/mcode_public_native_test.go": "f889bc0db3cf5f4d6c6f2b809c55ebbe21e0f8c1ddeee4258b6969fe93a82780", - "services/agents-api/internal/store/mcp_credentials.go": "6f916e25af9244b73ce04c23a0a2bdecec6eabf05a0e0e179b2cca8729605346", - "services/agents-api/internal/store/mcp_credentials_test.go": "82d90698331ebc5527bc797db42b946b77c9a64600149d43e03a5db396ed6531", - "services/agents-api/internal/store/native_daemon_test.go": "0e40f42cbdffef1b0b7f102bb6d7ebc66cc3663e3e5af52b06e0855581ce152a", - "services/agents-api/internal/store/native_daemon_workspace_directory_test.go": "9fd011a4af944445f44eb9dec337b9e51db878857952bc9c8af78870d47ecadf", - "services/agents-api/internal/store/native_daemon_workspace_read_test.go": "5a264adb7d7cb482fad9053f794b7425e235b3d6de3c6b385a9095884b76201a", - "services/agents-api/internal/store/native_environment_adapter_helpers_test.go": "ac98d5ce5dd4955ff7bc832d6e99f9e419dad7461cbf0b9844ed49f946530333", - "services/agents-api/internal/store/native_environment_adapter_test.go": "76e3549c2ec3c0530188a892f42294d01cb3047a8c797aa79542f1ab2b7bb787", - "services/agents-api/internal/store/native_environment_test.go": "9056b8ae057f07f739234fd9f0cd524746b522a5f69080022eca589647a62f90", - "services/agents-api/internal/store/native_executor_directory_test.go": "48e090c9f7b78da0d7b9db6dac1880fd554c30a33e75799cba928c08a9218e53", - "services/agents-api/internal/store/native_harness_artifact_test.go": "78bddaa1b173d450ad346c7ffde6cbc467312bd6d2c92e3a3751ba62406097e5", - "services/agents-api/internal/store/native_harness_directory_test.go": "50de6473955f216d8dea831beba5e0337ee42c8d680281539c524d811bc1a06a", - "services/agents-api/internal/store/native_harness_read_test.go": "91276e51273637fba6e27b4dd677e44b7553eb76ca0be9d789b6fa48bed94fdf", - "services/agents-api/internal/store/native_harness_write_test.go": "b83c12b131266cb88c4c685b3c879e31852c5a6088c46035ee175c4090852d72", - "services/agents-api/internal/store/native_placement_test.go": "9ea7ee8404861bdf39938cd362acc3e7611bb62c8048fe27040480cb3dcb30b3", - "services/agents-api/internal/store/native_preparation_helpers_test.go": "2ce87f38e3507e148b6eaa45fcf871d47318f18c321c49b60a5cbe94a785f68f", - "services/agents-api/internal/store/native_public_execution_test.go": "fc3457ac2d991e5e7dabea0bbb266e78cff126436ccbf323f1d5c3bf183ed3b3", - "services/agents-api/internal/store/native_raw_files_cancel_test.go": "88ce4b31d9ed6b3674730b41c568d602ea1eb2aaf71fbc065060a15aeb066a24", - "services/agents-api/internal/store/native_recovery_test.go": "e314ee55c0ec870da1aba5a9e906175c26d596c2252b6c53dd16b063db3d1731", - "services/agents-api/internal/store/native_relay_test.go": "660a6c0a3232a64b77242b711da0b6e08e194d4f5dfc836239bce9917c71a494", - "services/agents-api/internal/store/native_shared_files_test.go": "736ce887ac7e719b3d62116245807da5541f428ece8418428e8fdeb1e9398dfb", - "services/agents-api/internal/store/native_workspace_preparation_test.go": "cab0d1ee4232cdeb9e65b721efb184c28f5fe45f78cc80a16d5e8e3b84d394c3", - "services/agents-api/internal/store/no_environment_test.go": "cb8cea9e1f4091c91b24046b9066a7b387fcabccb880e40940f5f97c8f01dbc9", - "services/agents-api/internal/store/prepared_dispatch_failure_test.go": "155ad6164c2dcd4e25a496bae3d09093fe32eef36aeb0e1f4ecd3e520caf0a03", - "services/agents-api/internal/store/prepared_dispatch_native_test.go": "c8fad393cc79ee67fb82886b4b90de64b70135dc9cdd37dfd7215ea884c1c3fb", - "services/agents-api/internal/store/prepared_dispatch_public_helpers_test.go": "ccbf4096dd5d5c927439fe0c2c5751fe3138d98b49643eb1f19947a05827dc36", - "services/agents-api/internal/store/prepared_dispatch_test.go": "f29c9f1da7a18bbfe63246f06eb1f7a4baf496e8af6a70bb51ae59ac89900e57", - "services/agents-api/internal/store/project_scopes.go": "5bfd7f6bac7479020a5d0c16afcccb2f5c399ea1f89a0e8183a13f794c58b805", - "services/agents-api/internal/store/project_scopes_test.go": "796f4f175db6e98ebe83b90d854ed071fd70aa2d686dbacee8c33bd4b20a9e50", - "services/agents-api/internal/store/public_execution_test.go": "30b438ecb24af854e34e73b403e6ce7607423363d7461cd84f8d6ba6ab0701cb", - "services/agents-api/internal/store/public_harness_profile_test.go": "134dd0538725310b0c534334d405b4c81f9db5c77f1eef59ac4e31a195155b6f", - "services/agents-api/internal/store/remote_mcp_credentials_test.go": "c31685d9587138e6171885fd544a8ad24d8181cf5bf00f765ee3ce8fe02b9c9a", - "services/agents-api/internal/store/remote_mcp_test.go": "0c31cc3ce051b799463034f84f71383f62f61c21acd81dfc7f1f4946f07e4a16", - "services/agents-api/internal/store/runtime_allocation_state.go": "bc17b6698edcc75c0874078fb6942f028fb784e12ce61cf1d33c9e59f2638211", - "services/agents-api/internal/store/runtime_allocations.go": "15d4a05c3a7967dbd0e4f578ee91d2a8ad38c3d65f8320b09160bff5769679bf", - "services/agents-api/internal/store/runtime_allocations_test.go": "0660be1097516a89c2a3ab68f0e37f5697a0c150e70960d9368efbf7981b929c", - "services/agents-api/internal/store/runtime_connection_test.go": "acd796e3c048f79212f6cd516a7e2c2d3ce7ce7a0b967d1655da8f6e65391d1a", - "services/agents-api/internal/store/runtime_environment_terminal.go": "bce74bbe117d962242c7f7a86ac70f7e121f18cf7f5338ccb8f894736daf9df2", - "services/agents-api/internal/store/runtime_environment_terminal_test.go": "030f61dd0d26a78d8105b9c2ddd6a7149929e1483ee7f9db146180199b43298f", - "services/agents-api/internal/store/runtime_initialization.go": "c5db894a77545bd3ed4cbfe4badddf6aba15714654431d8d1cb06da7f7915396", - "services/agents-api/internal/store/runtime_initialization_test.go": "90c5e15dbc718e586cf24701fae05467b3de0db470abc795db5eb10881c5c73a", - "services/agents-api/internal/store/runtime_input_admission_test.go": "bd5106a092f9bc57e69c3ae0eb608b3bfacfee632c00d0cb7e2db08be2c4e958", - "services/agents-api/internal/store/runtime_lifecycle_test.go": "709ccbb32441d5cbb8561de6ccebf68c06ecce4e7339c4f4ee448a94071dd44c", - "services/agents-api/internal/store/runtime_pending_test.go": "8714f33baaef592fae016e2a67cfcc61146a0cd34896ad5b7e1d570cf4f8aa42", - "services/agents-api/internal/store/scheduling.go": "4e4fbd44e03464cccbf9740e2adfeb6b4da67079f495d45a054d420140e7a4b1", - "services/agents-api/internal/store/self_hosted_cancel_public_test.go": "c57ca55b46b47a2e533314639aa60ea36fa7609e4158f89da40a144d41bd4706", - "services/agents-api/internal/store/self_hosted_functions_public_test.go": "1c0f61adbe5d29f81ba4bef8936181695cb226528e01ae791ac72585c9d245e2", - "services/agents-api/internal/store/self_hosted_initial_public_test.go": "9d16472f94229bbb008099cce62263ad62cb99bdb4e6c706a3e1779776bcfcb3", - "services/agents-api/internal/store/self_hosted_public_cancel_native_test.go": "77f7305ca2c93c9b3417ad9807623b359e9d11b5863b267ff4a38803af19b7c5", - "services/agents-api/internal/store/self_hosted_public_fixture_test.go": "e38c45ee90509c209cba6f3bfdf51d04b71741bcadd80160c13200fa34493ae0", - "services/agents-api/internal/store/self_hosted_public_functions_native_test.go": "8f983b54e26062ae6136a088467c7c2f25e8de9b0881da6c5ec2447c9c4f6dd3", - "services/agents-api/internal/store/self_hosted_public_helpers_test.go": "e9b46f47ce1fd548e093cfbceca8db6f7c7d0836e9da97004c19260de389e463", - "services/agents-api/internal/store/self_hosted_public_native_test.go": "d253c40f8f007a6d4876ecb645f31b8a45ddebfc1c1416d15cd55134a5f6d3a1", - "services/agents-api/internal/store/self_hosted_public_steering_native_test.go": "779967411ba5b068c7cea6c1e423a142dab2f3239cbd90c724ffbdc0e1b49eaa", - "services/agents-api/internal/store/self_hosted_steering_public_test.go": "b069d39f1edb6b7c7370a298951fe4a2e12c5923881665a6d62df085fd0041ef", - "services/agents-api/internal/store/session_agent_filter_public_test.go": "fdfdbbaa07af00cf0ae6c2c7a6e38cda6bab23543e75a4139764eaeae1f35c83", - "services/agents-api/internal/store/session_agent_filter_test.go": "cd072123d1b79548fd08ac747e353932ab3cee84fe71e146b606009ffbc1f65e", - "services/agents-api/internal/store/session_artifacts.go": "4b5618965ea184e85e440ce3cdcb80898ac25821d769c1e347925c4da633f1fb", - "services/agents-api/internal/store/session_artifacts_test.go": "0535a8e0200fcff8935dee9e5de9afbc4eed5fcc05f526d1e1b5606117aa49d8", - "services/agents-api/internal/store/session_configuration_test.go": "51104afa2a4076efae5922f7f972366eb4f2f20fbd0af53e16ce15c32988fa20", - "services/agents-api/internal/store/session_creation_identity.go": "f45e501103b92ab88eb91f02a1b9c0b3c09e6ed31444fb327d03a084a7d2a589", - "services/agents-api/internal/store/session_creation_identity_test.go": "bc9d6a443f7d1bbd446ee9165d9356e2b69251373de2dec7c6a10360dcc47d31", - "services/agents-api/internal/store/session_creation_stream.go": "2479f7b9cf21ba510d1e20101cfe6c130990e69395182c5ded38ab6283f654df", - "services/agents-api/internal/store/session_creation_stream_test.go": "a3f3f6097e8740bc7a3ad5bced3cd9f29ce17037a54caae657857daacb8ce2b8", - "services/agents-api/internal/store/session_creator.go": "59373f566dd26d64fe1cb1e122757bd1aa50ab03ebb8bba81d1d5e38f527cf4f", - "services/agents-api/internal/store/session_creator_test.go": "929cc7bf1f245ba4826b17da270dd16547e209511076913de565e207046773f2", - "services/agents-api/internal/store/session_deletion.go": "08ccb5aa1359cdb3d3f43c270eb6fe12b643e28eceb2d25bab755723108d9f1d", - "services/agents-api/internal/store/session_deletion_execution_test.go": "1106c804fe9498fc24ffbae3a360a6d6e9ff9a9d87cca26be4299a44ee2205c5", - "services/agents-api/internal/store/session_deletion_public_test.go": "47fdf7a28746d16cbde93c72d855e132911ea8921b9bea37f7b887d603a3f8be", - "services/agents-api/internal/store/session_deletion_test.go": "f7085bbc9d5810c614341c0f705bf655e744ce0b59eb3bce09f72be29268469f", - "services/agents-api/internal/store/session_environment_snapshot.go": "407b4e11ac054e1eccde450071e89efd751a55b740509b5b2efce5d77b0c9ab7", - "services/agents-api/internal/store/session_environment_snapshot_test.go": "4557745b3a6e715b6d8506287401756f7e4c99eef11857005a508bfe0885e368", - "services/agents-api/internal/store/session_events.go": "e9efac9c1a6450670e1319ac6f834289e07ca1bf60511dfcc2b3a80c3b990ab0", - "services/agents-api/internal/store/session_events_test.go": "daacb0237634b3269f29f0f4d07e1cb191ff2b898fa98fe35c9f32d747e3fe9b", - "services/agents-api/internal/store/session_initial_input.go": "26f3298bf3c34eaa226b3ded8650b6e8432d9727d44c5a2d52667bf5fc1e46ba", - "services/agents-api/internal/store/session_initial_input_test.go": "92d331cc0e17ab3a73776355b53a5c2aa707af4e56b2b09a77fb495a5cc15fe3", - "services/agents-api/internal/store/session_initial_public_test.go": "1b6b9a2e6c206786ff3f2d4c9ad41fdfddfa70d6b3d0144cb18e489fde208de3", - "services/agents-api/internal/store/session_metadata.go": "32e57c317c38ade9c9a1a380c2fea0698e7d7008f90022fedbe9a5066584c773", - "services/agents-api/internal/store/session_metadata_test.go": "c464207273975e63337d9f6fc9b5ac4f435c25db82fcf020cdb6917be99d545c", - "services/agents-api/internal/store/session_model_execution.go": "80f4ffd713beb39ffbbd4f29c90cb90174c63666d7da656db22023efe71620da", - "services/agents-api/internal/store/session_model_execution_http_test.go": "11718a19a2a5cd43229eeb05e5d813a73541c5e15a76d21dad602dac506c0be5", - "services/agents-api/internal/store/session_model_execution_test.go": "e6d7e4ec699a7c4e4cd57fe37006ee51f8f78435e092c18d69f38f4c591588d4", - "services/agents-api/internal/store/session_reference_retry_public_test.go": "276f0c6257ee58d5828dd02e44df6ea468b5301d6e12b5dde19476d2c41f0df3", - "services/agents-api/internal/store/session_transaction.go": "ce551fb75efba6a3ae0db37446ae5322529bb2c2d648ce60101f1c4bd08b00ff", - "services/agents-api/internal/store/sessions.go": "75b0916a183e624a0fe01b01838f60bc734daacefef5c5d8b9e2e622985598dd", - "services/agents-api/internal/store/sessions_test.go": "1e8c40add842ba9d8dc3e46ba73e02f5e08b550dd204fa053732d4eac4a54c52", - "services/agents-api/internal/store/source_file_writer.go": "07aef58cd94802a43e491018a893329eb2d420358d07910b3fded7764699f709", - "services/agents-api/internal/store/source_files.go": "ae056725fc20a520f80443b33cf7f89af56d8af8eec1cc8842e361f4f4a8d65d", - "services/agents-api/internal/store/source_files_list_test.go": "990bca3fce855fc45de6c767be32e539eaa51f7047315b0f103577664969d533", - "services/agents-api/internal/store/source_files_test.go": "d3916c6b53b15cac784c7a40cf69d53a00096b941cae743aeedce03fb61e4b10", - "services/agents-api/internal/store/steering_receipts_test.go": "cd10ca2e4c1f8f40db97a412a0493c2f3f32d30fb638cf4cfb39e6a8f60c7c09", - "services/agents-api/internal/store/subagent_dispatch_test.go": "7646c899ec445df5923b5a1b215ad5e06c2a32db1e0fb474b6f5afdf843f2a1d", - "services/agents-api/internal/store/subagent_identities.go": "e64eb82bad5c0382436efc8937dd36de3e51e2ffe1e31dfce4e002ab5f139f06", - "services/agents-api/internal/store/subagent_identities_test.go": "b84e85f06c090238997d7db66b2fc013e9169aec5eb89d03b35c0620d69b811e", - "services/agents-api/internal/store/token_usage.go": "a7eb84eacd2efda447c4f2324200b01ac962a3b36b479864466219e40fac8276", - "services/agents-api/internal/store/token_usage_integration_test.go": "5a7606e34d8c5a6eda775e2230980088b6d4ca51bbcd5ad8304dd5a2ebe76ae7", - "services/agents-api/internal/store/token_usage_test.go": "93b26f5a5a1e807010c0d3483176c65a21b72519c6c29756fa9efa2185f7ae15", - "services/agents-api/internal/store/turn_completion.go": "184021ffcb4abc0afe2847f467f318deb2061249a2b4b8f3cf308e38c1ea9555", - "services/agents-api/internal/store/turn_events.go": "1a9fc9d1c5b51f0d19c5cca16597f0e082356a7e4082a7206eb90715292fa4f8", - "services/agents-api/internal/store/turn_events_test.go": "1f4742f3521246db46030123b51c64e914ce9b07958261d8b01023d3e67319aa", - "services/agents-api/internal/store/turn_inputs.go": "8ec218e0e683456f2728da3600b4d19471adc5a36f9d294d718a4534f552b44b", - "services/agents-api/internal/store/turn_inputs_test.go": "4a4d16eff74a550137efb42c959e5a7f61a0fa2df2b6f9c611381cf8940798cc", - "services/agents-api/internal/store/turn_reads.go": "6c7a72baaf65b301d8b144d5e20f0dca35cadbf429ed98bc4c1cc4e81aa9726f", - "services/agents-api/internal/store/turn_reads_test.go": "e71e4a21c77ce8be8a5a42d0f1d38630638e5652e9d26b66daf649abc9b7ae60", - "services/agents-api/internal/store/turns.go": "d04c05b59209320293d02551f665d4b3efecdcf76a95b8ffacba7847a70e2a92", - "services/agents-api/internal/store/turns_test.go": "a35053fdc029ceb3ce18c423d7e7cb66478881dafd33a6ab07d10d03862c5f5a", - "services/agents-api/internal/store/vault_credentials.go": "44aff12d7d66c108e0223363b769bbaac820c61c7e2266fabf1c489160b1a7c4", - "services/agents-api/internal/store/vault_credentials_delete.go": "2184676a0bd9732b4e43d1d14e98d6c4a01dd0698636ae068ff9d5edfcd509d1", - "services/agents-api/internal/store/vault_credentials_delete_test.go": "8412676d5fe98769845fc0f06a2f73677a32052d142f1087c41938af834652e0", - "services/agents-api/internal/store/vault_credentials_list.go": "2a2a1504de746e480670ceda4a3b959f9e0bde6612026127b9a46c18162ea158", - "services/agents-api/internal/store/vault_credentials_list_test.go": "6b885921e3627e77211bf8a60914575fd80fb93063d0c684bfc2f77cafa7db94", - "services/agents-api/internal/store/vault_credentials_test.go": "ff7e0f0b206e25f8f362a35ad4fa28b957bac64418220d22e785cbfe1c763272", - "services/agents-api/internal/store/vault_credentials_update.go": "14bb7c955e801cbbc9a9c7c7c8146507d4717bd9a30ddbe8b291bdb775bbae2f", - "services/agents-api/internal/store/vault_credentials_update_test.go": "f0b41d235a4f89a3c6f32531b5b3f5d745b8aa3a36e94b05e739d2e7e1277b18", - "services/agents-api/internal/store/vault_status_migration_test.go": "59850e8d365824ab1b4f4abaf3f3847a2730f3c2169d87174c0afc936dea1ce2", - "services/agents-api/internal/store/vaults.go": "2389694c010a382353955d5b648e54966bd6b5af9241357fe45393f18c2419b0", - "services/agents-api/internal/store/vaults_delete.go": "358276e635a5a1dbfadbc1c15ecaf84a693ee4579d9b1a69d84866450429caa9", - "services/agents-api/internal/store/vaults_delete_test.go": "b30759817fedc5254bc22b2b601eeccb1e04d85638583f29a9c5a0348749b7df", - "services/agents-api/internal/store/vaults_list.go": "61a189618ab389fb347527193c7d1c8fb0194edc7ec9f54186fe704e6e3cc24d", - "services/agents-api/internal/store/vaults_list_test.go": "7025ba65ec0e4086a94933ac2b2673c971e3ac2bb342be82a501de87b89ef22f", - "services/agents-api/internal/store/vaults_test.go": "af9b97e8c8130e5a8c3749458057844875e8090a327142059e60a822cf7c9f4c", - "services/agents-api/internal/store/worker_lease_loss_test.go": "acd434474321bc770abbe2bd42994ce3dab89de3931eacda5e4239a0c6d524b8", - "services/agents-api/migrations/000001_sessions.sql": "87e5fa395b209cf672b28d2baa1e91a7b8d53a140517a511fa87c1ac890368e0", - "services/agents-api/migrations/000002_session_configuration.sql": "b89d174e4de8448efe821878c3cf609c48b8fda6e96afb0703392e84a457faaa", - "services/agents-api/migrations/000003_turns.sql": "b5acef0061c67d59db8b5a8b85c6bd41b8955dc2058a0c680690cdeb3e44895d", - "services/agents-api/migrations/000004_input_batches.sql": "634bb431c899711631d0ab71c8b6b6bb7706a6491d0dbfb77c8fe68abe546950", - "services/agents-api/migrations/000005_devices.sql": "2d9047b977777aa0025168a352e3d8c97adbe513fa8599e6d11129680879142a", - "services/agents-api/migrations/000006_native_sessions.sql": "bfb2dda52d421f10e3c0b410087f4726c0e39a01d432237935e18a3ed46bd3d6", - "services/agents-api/migrations/000007_turn_events.sql": "04ca80b3899cf6e72afed2dd014323c43e74653836a30eaae00683bb1d25b49f", - "services/agents-api/migrations/000008_session_items.sql": "9eb349f78f3dbea89d4a74be61cc83f813ccaacd4c99b51096b21924ce38c868", - "services/agents-api/migrations/000009_execution_queue.sql": "fbc088d0d5498ef35d77d2c1768e5e2473b82b13119343dd36e75f352cf6aa21", - "services/agents-api/migrations/000010_token_usage.sql": "1be25abb75031b5dd52c513b635191f25d45b185440b5dd7601c9faaca0a8e11", - "services/agents-api/migrations/000011_item_order.sql": "ca1fc9577d3ef221b687bc50c934539dff61150dbfb1dfe8b4ace78e0c698552", - "services/agents-api/migrations/000012_session_events.sql": "3ebab5ce6391bde2612e68697be52bcc657c72fbd0d619a1073f8907804677c2", - "services/agents-api/migrations/000013_function_calls.sql": "00a4b9ffb313913aeac9faff7036eaf860f999d5f3669d161efcc48d2992955a", - "services/agents-api/migrations/000014_function_inputs.sql": "adfd4f9ad6f8a2927ba416248483b82bfbe3e80e96e2c121a922135c4ceb3bc2", - "services/agents-api/migrations/000015_retire_item_backfill.sql": "d7da76c01b25d0d55edc93f45971bdc06855c45e10ebe832e293e38290e64431", - "services/agents-api/migrations/000016_agents.sql": "cd22df06354f557e4bfaea57d72faa51c86438b2da8ceece3c78855c8b64aeec", - "services/agents-api/migrations/000017_session_creation_identity.sql": "c2d9d0aac322b12e14e3c015cfcc99e529a6ea71244ac4b536fb58bb5e3dfb6d", - "services/agents-api/migrations/000018_session_agent_filter.sql": "365e3272cdbba8b3ff7e9d670bb5740fc9546725c7072ae83e3bf36bb97861ee", - "services/agents-api/migrations/000019_session_deletion.sql": "39f5705e19f1411b804ba8a31a38cf48f737ba7c981d453520b33808e2ac2b3f", - "services/agents-api/migrations/000020_environments.sql": "ee422d75e1299f978697ba8539b9f71962b8e3465a064f199f998a640e00bf1f", - "services/agents-api/migrations/000021_environment_executor_credentials.sql": "1c2f0475e4a1aeba34ae6d9701907be19a16ba4b860481d9eeaac345ad293e34", - "services/agents-api/migrations/000022_environment_input_reservations.sql": "522614ba43e6f1c589afd08f3e23141eaf3470767d0841bd3cc3bae20b780cf6", - "services/agents-api/migrations/000023_environment_input_expiry_index.sql": "e7188005cbde9e4770296d9e5a2fa912bbb2f5bbee71c33851624a2ef9c5ea3e", - "services/agents-api/migrations/000024_execution_project_scopes.sql": "9dcc8cceeb85765179ab01d506430bcd0ab7f5b99a3f709ccadbf149c209f27b", - "services/agents-api/migrations/000025_session_creators.sql": "e6ace5cc1517a93d8f2f7a9a79e1ea788f1c40d749999f118b94d82ff8d16ab5", - "services/agents-api/migrations/000026_executor_principals.sql": "8707b1c462832fc329c0d00ae7a17c3df1c8ae2b2263a063c4d8f22d3045c1d7", - "services/agents-api/migrations/000027_environment_connections.sql": "de5b0e634d32cdd19520644e0055b491ab5215341207b36c4745dcd7083ca086", - "services/agents-api/migrations/000028_environment_input_activity.sql": "2db018a82900685bec2a70e7aae298a7e9c67e75768d3f0c1902f7cd70367c88", - "services/agents-api/migrations/000029_environment_initial_input.sql": "9add7c0e18531defb2b7a6630a674cbc71c24aa4b2a5e7916477431e4d8ccbe1", - "services/agents-api/migrations/000030_vaults.sql": "cc36182806b7ea9e31e99a24c8f8f85aa3bc90cda1a2109d70a855babec1f339", - "services/agents-api/migrations/000031_vault_credentials.sql": "6cda2a64791a818928b48d57eab245ff97c3c35dc3453a0fb436188eecbf235c", - "services/agents-api/migrations/000032_vault_status.sql": "e253a98e5e0c84caf0664fa489ef65b07dd494d07af1b86964d3abe38ceb8ee1", - "services/agents-api/migrations/000033_credential_status.sql": "df3cae1bb8705891b0839e74f1812a8bfa245804055ea5695e06b4784cb4a6c5", - "services/agents-api/migrations/000034_subagent_identities.sql": "20a313d5a097c2c534e37fb83323ddc11cd9ae0c4d4d35e9527624fc545beb58", - "services/agents-api/migrations/000035_local_environment_devices.sql": "3616955188ed8569d149f6a3c7bd912a5fdc1490cf98713745be3f1b28f460d5", - "services/agents-api/migrations/000036_environment_file_writes.sql": "c74849c6c108fa9538ac13028f3a2e471e23a40b86df88507877e44dcf9434b7", - "services/agents-api/migrations/000037_source_files.sql": "a41f5bf8da2bd05214008576d5bafb13bcf6ba2af5f7a51043f337212403b9b9", - "services/agents-api/migrations/000038_runtime_allocations.sql": "5d7850d0fe0b8ac19b3905a78e661e2b45f66f31ba4f9acdbc626412f15e7a9e", - "services/agents-api/migrations/000039_environment_input_failure.sql": "26e293c5f3cbc5a814f0ed2c073fcfe61ceadc83362e944fe1d136d06bad5d8c", - "services/agents-api/migrations/000040_session_artifacts.sql": "04a1955769dfe1178c23bdc1267e2a2da03897c76f0a1144a923dea2a8c48013", - "services/agents-api/migrations/000041_source_files_list.sql": "0ff89549c4726e60cd4372d96450e931ac79cc5e74e77eaf32cc46e71b186d7f", - "services/agents-api/migrations/000042_environment_templates.sql": "152a0adf46c6d59c04b1f6b131f7454d0ce23c26003c897c6fd1de5e2d7b035e", - "services/agents-api/migrations/000043_environment_initial_files.sql": "b9e496284137dde698e46a24104fb0b987cfa6adfcaa4682afbdac2cd9ff693d", - "services/agents-api/migrations/000044_environment_setup.sql": "67f4f3a0c92a5ecbedfda5eb4a36385a24876879eed16b90725892e57bc0b4ce", - "services/agents-api/migrations/000045_environment_skills.sql": "cf20f5df6a19c2b9ca2a573098d499036026cf0142d023bd2a4b29d6387a1b8c", - "services/agents-api/migrations/000046_session_model_execution.sql": "092ae45a54da7147d1e18fbcaeee955cbb496974c830368d9e89902403d7a377", - "services/agents-api/migrations/migrations.go": "c85742b2bf14111e14e1948e846ddf6ef37e0cd6f174ff5f1bdd85ba34a08e7f", - "services/agents-api/sqlc.yaml": "e959acc830de8934258aadcf039613f246f99e3e1d6a61e2d434d4646ce0a74d", - "services/agents-api/tests/container_server.py": "4fe3052e7b487036d6c386292905c2802eaad6f29eed1c86cb7ca33a344859be", - "services/agents-api/tests/e2b_native_isolation.py": "f34b0b8bf2c58de08e162b2a5addc330ca05bc6a95217428b5ebb965eecd7df0", - "services/agents-api/tests/fixtures/credentials.go": "2b3d9b4e62a08d79eb5d6d1fc98c20a20f31a19bc40f99056b6e5dfc8fadf1ce", - "services/agents-api/tests/fixtures/items.go": "ddc865b5cdeae54f6ba913a1bd26dd6e6b800c18e4da9de06b9c0ec79db292b7", - "services/agents-api/tests/fixtures/main.go": "c7c99cba138d7eba7202c8817a520fdcb5634540a4998a44ab76c9b7b584935e", - "services/agents-api/tests/fixtures/vaults.go": "aabd87fb1e8a415e6eb745bd08877400f4749afcfba78bff7fc699e344598601", - "services/agents-api/tests/native/README.md": "c05b5f8bc10da26569b4bd109112474a5553bdf305966890dd9a429444b9467d", - "services/agents-api/tests/native/directory/README.md": "cbc860e517be38db6a5500f051806aa77a1038ea38eea3075b890f62c20e8419", - "services/agents-api/tests/native/directory/probe.rs": "ad54841d6a4f0f57612c80fe22f60dcd27ba0e1491f2d1b097ffeae1c9757808", - "services/agents-api/tests/native/environment_model_probe.py": "e39dba231e3a3b4c770bbd8209c1673f76197b6b070614049ced08c67af0a258", - "services/agents-api/tests/native/raw_files/.gitattributes": "f3ed2a3cc878474ef09273325e3548a6a22444712f7123f70f776573457e5a28", - "services/agents-api/tests/native/raw_files/README.md": "aa8a311bb4d4f2aa2a7fb03310355da0c082f7efab64cf5ba068aae3dc1d7603", - "services/agents-api/tests/native/raw_files/client-dependency.patch": "76df7fcb970ca67bd1f40b2d98e5a2f8ec24564f2e772a69a7e9e9b7ad7f2cc9", - "services/agents-api/tests/native/raw_files/source.json": "15fccdeaaa7cc8ce28c3ff7d4bb32cc7d25d015a9faaf3e5db82fc86f9070185", - "services/agents-api/tests/native/raw_files_probe.rs": "de227317c4058c5a2c9b78b46ac68593178501d5fea01f0f382b286fc832f23c", - "services/agents-api/tests/native/raw_manager/.gitattributes": "7943cd6175021a7788c79f348f373e797d93b8d3360aebddbf15e82d73e301d6", - "services/agents-api/tests/native/raw_manager/README.md": "2a009cfc1981d160f6388dabea409ceaed80851d9d16e6448e391ee5a9cc82db", - "services/agents-api/tests/native/raw_manager/owner.rs": "609af91aa100bc4117c0b8cc8eddf9ff7b44ac62f30245d715b374fe20ed5648", - "services/agents-api/tests/native/raw_manager/prepare.py": "24493bbc7bd01baee6218830dc858078aaa32bd4cd5490c66414786bc2b85a18", - "services/agents-api/tests/native/raw_manager/probe.rs": "45a14e5edf5338ddbf2a7a027860dd365d00c353e35cd6637d583efa6b525deb", - "services/agents-api/tests/native/raw_manager/source.json": "092db927945a4fff31ca98f024f0430329dfa21478606f412ad03cd6f9eef59b", - "services/agents-api/tests/native/relay_probe.rs": "88c6f738b0350b0a34cce3ead8a9c63916b82774e76161c984a88903add8d4a1", - "services/agents-api/tests/native/retirement/README.md": "7f234bd574cbe066e92d1f632d1dad2e9b4c0e26b4ce19eb7359133348edeaf4", - "services/agents-api/tests/native/retirement/gate.rs": "dafc1f9af93f5e9d15c0420b7451d0ec27d85f5c0b85d347b17dcfc8c95cabe5", - "services/agents-api/tests/native/retirement/operator_retirement.py": "ee0dec54410e4ff4af03d002678924632705fc476bde406d210d960803c5818f", - "services/agents-api/tests/native/retirement/placement.py": "f2bc682a609f0d9696d74ff907decc4d9a620d08baa1c31b0c71eef0f10438d4", - "services/agents-api/tests/native/retirement/placement_test.rs": "65362ddf7e40fbb4b6a303dbe1c28a094460df164fe891270b6a05262a0798d6", - "services/agents-api/tests/native/retirement/processor_test.rs": "33bf5cf8eac4767bd8d90c7317d3d7ea1545c9ac5d15ecacf4709f2bc9980524", - "services/agents-api/tests/native/retirement/qualification.patch": "1d6d990cd026f9299ac4e4eef401280e1b5ef6a98806c90115e14f86af00d32b", - "services/agents-api/tests/native/retirement/source.json": "4313c56a356b0daf2e7eab2f643752044d7bf3828c3f9a5bf55fa420a3a8a2c8", - "services/agents-api/tests/native/shared_files/cancellation.rs": "0d8bd2ce489c54b3c467b3b7ea2f1e6ccedc6eb9a28420ed46b5dfdab2c7b47c", - "services/agents-api/tests/native/shared_files/configuration.rs": "45f373a225fccb8d74d6f4c6c017c9b0e37287323fc50a78bea5d1647557f482", - "services/agents-api/tests/native/shared_files/files.rs": "e8ddf3024dba712256fe555363773380cee057947755ba5917e1d8d0010ea17d", - "services/agents-api/tests/native/shared_files/observations.rs": "e7fbdcb0be409a6827088fc734aaf4b76eea425f2f697bb097a738019a5049e5", - "services/agents-api/tests/native/shared_files/probe.rs": "bf96841f708b75341b50ece67e234fb47aae71655ef5644de6edb01f5c5dec1c", - "services/agents-api/tests/native/shared_files/raw_runtime.rs": "90f9e5c1b72524fc5061e6f0bf174a6ff1dc08732c7636663eb948053bf2dbc9", - "services/agents-api/tests/native/shared_files/runtime.rs": "af33d924aae6076ac2fca6a1b481bc578f134cea21d3f02b8a77681df230314c", - "services/agents-api/tests/native/shared_files_probe.rs": "9390424fc97811c49844ef09a1ac225fdc42695d8b485f8765af84b368ef778a", - "services/agents-api/tests/native/write/README.md": "c88a15a8e1a44bd851af7786f8d26eb3004a10aed6f185f80a09cdcd6fa2adff", - "services/agents-api/tests/native/write/probe.py": "9798b297f183e069654ee39c69a6acd8877026ae39707505d2f2e679d4d4a801", - "services/agents-api/tests/official_agent_delete.py": "68978f155344d43ededa15ead21d2358fea418e4f680674b2460f5ce742ab494", - "services/agents-api/tests/official_agent_list.py": "b7c559bca74b1c22549257aa16fe14aefd39eead084eb86de30a9977c57141af", - "services/agents-api/tests/official_agent_reference_retry.py": "4ea9159a63f9587b9465f5a17d6ca052aaf785f53c4071c929904c4777a29804", - "services/agents-api/tests/official_agent_references.py": "ebc501b4af147dd7aa280bf65dda0774ebaea6f202bdd6ede8efce352dc6b797", - "services/agents-api/tests/official_agent_update.py": "185fe7028c5434ad819ee5641866789581b219e920d5bf1f9f302f1d42ee3f9e", - "services/agents-api/tests/official_agents.py": "3be6a781effee0e7486ddbd7fc187fbc21897fdfc45fc887ed11434609dfcc08", - "services/agents-api/tests/official_auth.py": "630c093a19dcccd3a64aae7b2b009e72fb1b5dedfcf3a9185afa2b870723536b", - "services/agents-api/tests/official_client.py": "b4f4182e2cd543d9e8e68b8f8efce4f2d44133ce780dc64cf1d9579806a57829", - "services/agents-api/tests/official_credential_delete.py": "0404b767a077fe881fb283587649a8e837dc1a8c888fd8c73c6188b43e2343cd", - "services/agents-api/tests/official_credential_list.py": "1dc00a0c09178b3211b02150f1acc82e034c1a1ee8a787d8e6163fe412ff3325", - "services/agents-api/tests/official_credential_rotation.py": "4b1ac8457247a880f31b83dc2cd1b91641c94a5745b5cec624d501d0c52ac1b8", - "services/agents-api/tests/official_credentials.py": "1776558d4320712d297db49213bb511ee31d4f8e81bbd63c1b27694a7fcadb79", - "services/agents-api/tests/official_e2b_v1.py": "be4a8e5f8e5ef5f4bbdeade7fd48d5804a10eb59c5d80283afe91948314dab57", - "services/agents-api/tests/official_environment_activity.py": "ea20b4c6de48d5f2fbd86f7026ccfda33fe65c4896db985427bb735f7c50b30b", - "services/agents-api/tests/official_environment_events.py": "2117198aff01f34c29464930a6f9123a3114abb034406b8dfbc07965b3430770", - "services/agents-api/tests/official_environment_files.py": "ba2e07e3c29cbfbcc38ceb0024db3c068af9f0c6acdd515ec9683adf405351b1", - "services/agents-api/tests/official_environment_files_create.py": "38bdabef3336836ce48d670475508a453d325d545676c244a6b2c29c893d8db5", - "services/agents-api/tests/official_environment_files_native.py": "438d2486092119f9f93b6b0c36ce4d38ca85cfa01c2fe62cf0ed286b9bbc8f3b", - "services/agents-api/tests/official_environment_initial_failure.py": "5f87564b4ecf7057c28d0ecd65c95fa7a7d567719f840e7ad192ce05d056d4f6", - "services/agents-api/tests/official_environment_initial_files.py": "bc54f8f7c751a9956f206d1bf1052c509348ebf6dc1029d81d2fcc6d597bc597", - "services/agents-api/tests/official_environment_retrieve.py": "74f006c47ecf9eb78a40036b0f686a74908d84c4d65c56a0223d756b5efc6050", - "services/agents-api/tests/official_environment_setup.py": "65a893debdb1de875b1aa23ecf9e5df93a14dd25a285943d7d09ff10bd667617", - "services/agents-api/tests/official_environment_skills.py": "b7033d6e36ddddca6160c87bb62898af9080e0908b803406f223209932c5778b", - "services/agents-api/tests/official_environment_templates.py": "dbb376a511bb43e80af3e6306a3259e96979bde783c92daac272380335bc4417", - "services/agents-api/tests/official_execution.py": "36e4029d839c4b6c9d41847c9e4924c68a77669d2dd002a2cba01d90d06882a4", - "services/agents-api/tests/official_function_inputs.py": "742457515bafb59c79af0f1f26c443218fdcb48c64f91b9dbd01d76f947b8c25", - "services/agents-api/tests/official_function_state.py": "9d93e07408afc53b9d6b5f04ec53aa02a16baf470caa76fa3d46114a28f4bb8a", - "services/agents-api/tests/official_function_stream.py": "4e5252bbf61705ead3a51b6e7c12c6a172098f850676842ba59cc7bcb09c59c6", - "services/agents-api/tests/official_functions.py": "e15cb83c5c0179f7c286c535488a9f1aa4883a92b47bc65a116dca2cd7bd8528", - "services/agents-api/tests/official_hosted_functions_native.py": "ce1ddc3627a9c88a2f38a1b77eca6674c9c3b3b6c64e9b2313bf058d04b4189c", - "services/agents-api/tests/official_items.py": "d480f6ba654433975b2b063d9936a8c79291203ba97d6f2e9fea55f82b6c7ec7", - "services/agents-api/tests/official_mcode_native.py": "e28ff013b4c04de5d346ea265eac3dc7a7983ce67aa499c6ccbacc6e73a819bd", - "services/agents-api/tests/official_mcp.py": "8c3ba1c05eee88c35bc1807b5c31fc2716be167343097c39f04876549c729e07", - "services/agents-api/tests/official_mcp_credentials.py": "cbe92285ad8e9be5f0a43df98d7d17d69c0745e2cbfad7ef85002b55d2697519", - "services/agents-api/tests/official_self_hosted.py": "0e9fe697e5837bf91a10b3fdb2989a6e0e54e973492ff7367917e19aeae3c275", - "services/agents-api/tests/official_self_hosted_cancel.py": "b58bcffcf0605b78a8f68ca37a5ffbdde7e8c92ddc0aa1db6bdfa2b8374202d1", - "services/agents-api/tests/official_self_hosted_cancel_native.py": "a5fa896138a68fa83f3e3c50f547701d3feef840e7f55d0fb83967d1cdee419e", - "services/agents-api/tests/official_self_hosted_creation.py": "adae4eaabb42d7e434e5a346244b8175f76f0af666bb189de8401945ebf34d0e", - "services/agents-api/tests/official_self_hosted_functions.py": "4373f28ec341efb11df3248833d92dc34497e6fa26d1f0c38693333aa54d0a44", - "services/agents-api/tests/official_self_hosted_functions_native.py": "f70a9ccf4619beae5a635683f2d0645020135e0ae7aa5cbb668e4a78d24eb6eb", - "services/agents-api/tests/official_self_hosted_initial.py": "e1e5655bf363eb75f232f9f647e2253007ec98cc4b1d49e57ca84f71246ebbd9", - "services/agents-api/tests/official_self_hosted_steering.py": "6106d1335cabf253f923fcc2d02737bc8f959f71ef202459dd806eb89a9bcda6", - "services/agents-api/tests/official_self_hosted_steering_native.py": "a4a50fd663929e1bbcad05f77643bd2ec17cc4960e95012a896dde66fcd2bdb6", - "services/agents-api/tests/official_session_agent_filter.py": "b3b6ceb24a61ab14e4ea9b53c0e8bcdf2d1c0f719a376680d02f45efc1d1f25e", - "services/agents-api/tests/official_session_artifacts.py": "6686268515bce5a68235978eebdb22dff654229f48fa3bcd1f1eeca236b33e1c", - "services/agents-api/tests/official_session_creation_stream.py": "73757461b5bcf33be50c83ed3c4dfd8382ca27c579fd6339884baacaa29f3024", - "services/agents-api/tests/official_session_creators.py": "a4308a3828bcaca7b12353ee9de9f723e61ce16355e3e74d040e3b1a73c6ebff", - "services/agents-api/tests/official_session_delete.py": "8cb8581c290dda7c415af385ae9d16fef51da7de9000f44578292244c3bf0b82", - "services/agents-api/tests/official_session_initial_input.py": "0d407a667e3a4c324247d5f2a1e5c084f1c61f5413b5c0d1ed4efa49c2a60b91", - "services/agents-api/tests/official_session_metadata.py": "72e100c8dd765f190868c7e25c50c0905573461f147e5446e441de9b6a9947cf", - "services/agents-api/tests/official_session_requests.py": "e9db3211f5bda807da9e79b314d920fdb6086cf55fa478ca7a086c58b3e03876", - "services/agents-api/tests/official_source_file_list.py": "905a2a7ac165c6d3228bc4b3c7bff95e9a9420acada2f27ba9f9c2333820d93a", - "services/agents-api/tests/official_source_files.py": "8f291739b5489076b91e08f3204da0b15c2f385a1ac6937f8a8e22abe3047426", - "services/agents-api/tests/official_vault_delete.py": "a3644978f857e93a4c2259751cfae695a80dabe9d08c640ae752971867363f0f", - "services/agents-api/tests/official_vault_list.py": "59230e62feaecbf58acd3e8a9fd870e0a88afa289e5420b612158cd264b060b1", - "services/agents-api/tests/official_vaults.py": "335dcb6341a6d19996467f380eac3ab072ef88e02dbddba740fee68ed4e38e24", - "services/agents-api/tests/requirements.txt": "c189f9508164a727d37665dd09645f36a1ede5e71cc794f618d554550d7cb920" - } -} diff --git a/provenance/verification.md b/provenance/verification.md deleted file mode 100644 index 8d12dac63..000000000 --- a/provenance/verification.md +++ /dev/null @@ -1,67 +0,0 @@ -# Import acceptance - -Verified on 2026-09-21 against source snapshot -`72ab4d37d49245f15b63d34f5741780e540bcec0`. The destination comparison base is -the empty bootstrap commit `6973875`. - -## Environment - -Checks ran from a standalone copy on Linux amd64 (`zju_a100_2`), with Go -1.25.13, Node 22.22.0, pnpm 10.30.3, Rust 1.95.0, Python 3.10 and PostgreSQL 16. -Only the destination repository was present in the build directory. A dedicated -PostgreSQL container and database `parsar_agents_api_core_import_tests` were used; -no product database or existing deployment was involved. Dependencies used the -existing SSH reverse network path. Credentials remained outside Git and logs. - -The official Python client was installed into a task-local virtual environment -from `contracts/agents-api/upstream.json` (commit -`d7c41efee1b0802b79f3f88a678ef2052b06e9ce`). - -## Results - -| Command | Result | -| --- | --- | -| `python3 scripts/verify-source-copy.py` | Passed: all 1,287 imported paths present, 1,282 byte-identical; only the five documented packaging adaptations differ | -| `make check` | Passed, exit 0: sqlc regeneration, daemon/shared/API/client Go tests, real PostgreSQL tests, standalone API build, Claude SDK tests/package, MiniMax companion checks, Rust format/tests/Clippy and Codex Harness packaging checks | -| `make build-daemon` | Passed using a task-local absolute output directory | -| `make build-agents-api-release` | Passed from a clean committed standalone checkout (`3ba5c435b5ac9f9cece9bc9c21d979a1741796b5`); archive checksum, every member checksum and new-repository source links verified | -| `go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.12` | Passed, exit 0 | -| `AGENTS_API_SERVER_BIN=.../agents-api python services/agents-api/tests/official_client.py` | Passed, exit 0, using the pinned SDK and dedicated database | -| `AGENTS_API_IMAGE=parsar-core-import:72ab4d37 PARSAR_OFFICIAL_SDK_PYTHON=.../sdk/bin/python make check-agents-api-container` | Passed, exit 0: build standalone image and repeat the official-client suite inside read-only containers | - -The official-client suites exercise SDK and raw HTTP responses, generated/upstream -schemas, persistence, service restart, retries, pagination, principal/project -isolation, Agent/Session/Turn lifecycle, vaults/credentials and explicit unsupported -options. They do not establish live model or provider qualification. - -The first database attempt used a name outside the test safety allowlist and was -correctly rejected. The final run uses the required `parsar_agents_api_*_tests` -name; no safety check was removed. The task-local Python environment used pip 25.2 -with PySocks after its bundled pip failed through the SOCKS proxy. No system -toolchain or shared proxy configuration was changed. - -## Evidence and limits - -Private full logs are retained under -`~/.parsar/tests/parsar-core-import-20260921/` on the validation host and copied to -`~/.parsar/parsar-core-import/` on the development host. Their SHA-256 digests are: - -| Log | SHA-256 | -| --- | --- | -| `make-check.log` | `3777c991688d65c36f81c141f2a3b0b8d1084b2670f787bd405c3af5fd1f2d5d` | -| `official-client.log` | `29133bacfda0ff6c59b748726882557f9a56dea3744eab92927b8f8e3cf9969c` | -| `container-check.log` | `6ffb0814b617844d1b30db3e0dc21d6ba3f565137c7d384e029281cc00370ea9` | - -Live paid-model execution, E2B provisioning and opt-in native Harness builds were -not rerun for this source-only import. The MiniMax packaged-native-tools test is -opt-in and skipped without its native prerequisites. Existing implementation, -fixtures and historical acceptance evidence are retained unchanged; this import -does not claim to close their documented protocol gaps. - -The full import whitespace check reports existing whitespace in three Codex -patch files and the vendored E2B process proto. Those bytes are deliberately -preserved and verified against the source manifest; the new scaffolding passes -the whitespace check. - -The source Parsar checkout and its Core copy remain unchanged. No mx service was -reconfigured, stopped or deployed. diff --git a/scripts/check-names.test.py b/scripts/check-names.test.py index 0dcad8c35..3ce9e8608 100644 --- a/scripts/check-names.test.py +++ b/scripts/check-names.test.py @@ -29,10 +29,10 @@ def test_partial_exception_cannot_hide_longer_retired_setting(self): [(1, 1, "AGENTS_API_PORT")]) def test_exception_is_path_scoped_and_case_sensitive(self): - rule = self.rule("parsar", "provenance/*") - self.assertFalse(names.violations("provenance/source.json", "parsar", [rule])) + rule = self.rule("parsar", "history/*") + self.assertFalse(names.violations("history/source.json", "parsar", [rule])) self.assertTrue(names.violations("README.md", "parsar", [rule])) - self.assertTrue(names.violations("provenance/source.json", "PARSAR", [rule])) + self.assertTrue(names.violations("history/source.json", "PARSAR", [rule])) def test_detections_include_commands_settings_labels_and_display(self): for value in ("PaRsAr", "io.parsar.installation", "AGENTS_API_PORT", "CORE_CONSOLE_BIND", "AGENTS_CORE_WEB_ADDR", diff --git a/scripts/core-distribution-manifest.test.py b/scripts/core-distribution-manifest.test.py index 19d755fa0..6589e3967 100644 --- a/scripts/core-distribution-manifest.test.py +++ b/scripts/core-distribution-manifest.test.py @@ -354,7 +354,7 @@ def test_repository_docs_are_self_consistent(self): def test_repository_markdown_links_resolve(self): repository = pathlib.Path(__file__).resolve().parent.parent - names = subprocess.run(["git", "ls-files", "-z", "--", "*.md", ":(exclude)example", ":(exclude)provenance"], + names = subprocess.run(["git", "ls-files", "-z", "--", "*.md", ":(exclude)example"], cwd=repository, check=True, capture_output=True, text=True).stdout.split("\0") self.assertIn("README.md", names) distribution.docs(repository, self.bundle, REVISION, names=[name for name in names if name], files=()) diff --git a/scripts/name-allowlist.json b/scripts/name-allowlist.json index 721dd2745..02811f914 100644 --- a/scripts/name-allowlist.json +++ b/scripts/name-allowlist.json @@ -9,11 +9,6 @@ "regex": "~?/\\.parsar/remediation/[A-Za-z0-9_./-]+", "reason": "Historical remediation artifacts retain their evidence paths." }, - { - "path": "provenance/*", - "regex": "(?i)parsar|\\bAGENTS_CORE_WEB_[A-Z][A-Z0-9_]*|\\bAGENTS_API_[A-Z][A-Z0-9_]*|\\bCORE_CONSOLE_[A-Z][A-Z0-9_]*|\\bagents-api(?:-(?:migrate|device|environment-key|e2b-provider|microsandbox-provider|tool-root|codex-directory|codex-write|workspace-export|runtime-initialize|claude-shell-prefix))?\\b|\\bcore-console\\b|\\bagents-runtime-|\\bAgents? Core(?: Web)?\\b", - "reason": "This directory is the immutable source-copy audit trail, not current installation guidance." - }, { "path": "packages/agents-client/src/*", "regex": "(?:\\./)?fixtures/parsar-[0-9a-f]+/[A-Za-z0-9_./-]+", @@ -249,11 +244,6 @@ "regex": "parsar(?:-server|\\.example\\.test)?|PARSAR_RUNNER_TOKEN", "reason": "The dormant product capability upload/download integration locates the separate product CLI and checks its product credentials; it is explicitly retained." }, - { - "path": "scripts/verify-source-copy.py", - "regex": "parsar-core|apps/parsar/", - "reason": "The source-copy verifier records repository provenance and forbids importing the separate product source directory." - }, { "path": "internal/runtimecrypto/cmd/emit-fixture/main.go", "regex": "Parsar runtime credential envelope", @@ -301,7 +291,7 @@ }, { "path": "CONTRIBUTING.md", - "regex": "copied from Parsar at|in Parsar\\.|apps/parsar/|Parsar is an ordinary client|Parsar integration|Parsar owns|`parsar` provider slug", + "regex": "in Parsar\\.|apps/parsar/|Parsar is an ordinary client|Parsar integration|Parsar owns|`parsar` provider slug", "reason": "These exact phrases refer to the separate Parsar product, its ownership or historical source, not the OpenAgentCore brand." }, { @@ -349,11 +339,6 @@ "regex": "~/\\.parsar/core", "reason": "A default install refuses while this path holds an installation of an earlier release; the troubleshooting row names it." }, - { - "path": "scripts/verify-source-copy.py", - "regex": "apps/parsar", - "reason": "The source-copy boundary rejects the separate product application directory." - }, { "path": "services/core/tests/official_environment_plugin_mcp.py", "regex": "'PARSAR_'", @@ -444,11 +429,6 @@ "regex": "https://github\\.com/MiniMax-AI-Dev/parsar/pull/[0-9]+", "reason": "Historical acceptance evidence links to pull requests in the separate upstream Parsar repository." }, - { - "path": "scripts/verify-source-copy.py", - "regex": "(?:services/agents-api|apps/parsar-daemon)/[A-Za-z0-9_./-]*", - "reason": "Source-copy records keep their upstream paths; the verifier maps them to the current directories." - }, { "path": "services/core/migrations/000015_retire_item_backfill.sql", "regex": "services/agents-api/README\\.md", diff --git a/scripts/verify-source-copy.py b/scripts/verify-source-copy.py deleted file mode 100644 index 975d520ac..000000000 --- a/scripts/verify-source-copy.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 -"""Audit the initial source import against its recorded original hashes. - -This is an import acceptance command, not a gate on future Core development. -""" -import hashlib -import json -from pathlib import Path - -root = Path(__file__).resolve().parent.parent -manifest = json.loads((root / "provenance/source.json").read_text()) -adaptations = { - "go.mod": "Remove unused product dependencies without changing module identity.", - "go.sum": "Regenerate checksums for the standalone dependency closure.", - "pnpm-lock.yaml": "Keep only the standalone Claude adapter workspace and its dependencies.", - "services/agents-api/RELEASE.md": "Resolve new release revisions in parsar-core.", - "services/agents-api/HOSTED-RELEASE.md": "Resolve new release revisions in parsar-core.", -} -# Records keep their upstream paths; these copied roots now live under new names. -moved = { - "apps/parsar-daemon/cmd/parsar-daemon/": "apps/daemon/cmd/oac-daemon/", - "apps/parsar-daemon/": "apps/daemon/", - "services/agents-api/": "services/core/", -} - - -def destination(name): - for source, target in moved.items(): - if name.startswith(source): - return target + name[len(source):] - return name - - -errors = [] -unchanged = 0 -for name, expected in manifest["files"].items(): - path = root / destination(name) - if not path.is_file(): - errors.append("missing: " + name) - elif hashlib.sha256(path.read_bytes()).hexdigest() == expected: - unchanged += 1 - elif name not in adaptations: - errors.append("unexpected modification: " + name) -for name in ("server", "apps/web", "apps/parsar", "packages/cli", "packages/opencode-plugin", "infra"): - if (root / name).exists(): - errors.append("product tree included: " + name) -if errors: - raise SystemExit("\n".join(errors)) -print(f"Source import verified: {len(manifest['files'])} files, {unchanged} unchanged") -print("Permitted packaging adaptations: " + ", ".join(adaptations)) From c915616da03576c45c56e3582a5e08753a03e930 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 08:28:23 +0000 Subject: [PATCH 2/9] chore: delete core-doctor and the Web /v1 development proxy scripts/core-doctor.mjs had no caller besides its own test. The Vite /v1 proxy, its bearer token file (~/.oac/dev/web-token) and the __OAC_WEB_DEV_PROXY_AUTH__ flag existed only for it; the console never calls /v1. The browser acceptance guard now records /v1 requests in the browser, since the development server no longer forwards them. --- .env.example | 10 - Makefile | 1 - apps/web/README.md | 2 +- apps/web/e2e/access.spec.ts | 4 +- apps/web/e2e/console.ts | 13 +- apps/web/e2e/fixture-console.mjs | 9 +- apps/web/src/lib/vite-auth.test.ts | 77 -- apps/web/src/lib/vite-config.test.ts | 11 +- apps/web/src/vite-env.d.ts | 1 - apps/web/vite-auth.ts | 80 -- apps/web/vite.config.ts | 21 +- package.json | 3 +- scripts/core-doctor.mjs | 905 ------------------ scripts/core-doctor.test.mjs | 838 ---------------- scripts/fixtures/core-doctor/agents-list.json | 7 - .../core-doctor/daemon-status-absent.txt | 4 - .../core-doctor/daemon-status-paired.txt | 7 - .../core-doctor/redaction-corpus.json | 11 - scripts/name-allowlist.json | 25 - 19 files changed, 23 insertions(+), 2006 deletions(-) delete mode 100644 apps/web/src/lib/vite-auth.test.ts delete mode 100644 apps/web/vite-auth.ts delete mode 100644 scripts/core-doctor.mjs delete mode 100644 scripts/core-doctor.test.mjs delete mode 100644 scripts/fixtures/core-doctor/agents-list.json delete mode 100644 scripts/fixtures/core-doctor/daemon-status-absent.txt delete mode 100644 scripts/fixtures/core-doctor/daemon-status-paired.txt delete mode 100644 scripts/fixtures/core-doctor/redaction-corpus.json diff --git a/.env.example b/.env.example index ee396f69d..1171e75d8 100644 --- a/.env.example +++ b/.env.example @@ -2,19 +2,9 @@ # browser JavaScript. OAC_WEB_DEV_PROXY_TARGET=http://127.0.0.1:8091 -# Legacy development proxy only. The console never calls /v1; the Vite server -# still forwards /v1 with this Project API key for older tooling such as -# scripts/core-doctor.mjs. Plaintext bearer file read -# only by the local Vite server; if unset, it checks this conventional path. -OAC_WEB_DEV_PROXY_TOKEN_FILE=~/.oac/dev/web-token - # Core reads its own environment, not this file. Core settings, including # Runtime history sampling and export, are in docs/configuration.md. -# Alternative server-side value for the legacy /v1 development proxy. Never use a -# VITE_ prefix, and do not set this together with OAC_WEB_DEV_PROXY_TOKEN_FILE. -# OAC_WEB_DEV_PROXY_TOKEN= - # Public, non-secret opt-in for the reviewed Codex self_hosted Session profile. # Leave unset unless Core execution, its executor registry, and executor origin # are configured. This flag is presentation policy, not capability discovery. diff --git a/Makefile b/Makefile index a9ecabffb..ea8f24e83 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,6 @@ check-web: check-web-unit check-web-acceptance check-web-unit: node-deps pnpm typecheck - pnpm test:core-doctor pnpm test:web pnpm --filter @oac/web build diff --git a/apps/web/README.md b/apps/web/README.md index 7db38237d..a558f5bae 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -23,7 +23,7 @@ OAC_WEB_DEV_PROXY_TARGET=http://127.0.0.1:18092 pnpm dev:web Open `http://127.0.0.1:4173` and sign in with the fixture-only key `fixture-core-key-3f9a2c71`. -`pnpm dev:web` runs Vite on `127.0.0.1:4173` and proxies `/console`, `/node-install` and `/core/v1` to `OAC_WEB_DEV_PROXY_TARGET` (default `http://127.0.0.1:8091`). Vite reads the setting from the environment or the repository's `.env` file; it never reaches browser code. The target must serve the console routes. The development server also forwards `/v1` to the same target for local tooling such as `scripts/core-doctor.mjs`, adding a bearer token from `OAC_WEB_DEV_PROXY_TOKEN` or from the private file `OAC_WEB_DEV_PROXY_TOKEN_FILE` (default `~/.oac/dev/web-token`, used when it exists); the console itself never calls `/v1`. `apps/web/e2e/fixture-console.mjs` is a synthetic console service with deterministic data; `AGENTS_FIXTURE_PORT` changes its port (default 18092). +`pnpm dev:web` runs Vite on `127.0.0.1:4173` and proxies `/console`, `/node-install` and `/core/v1` to `OAC_WEB_DEV_PROXY_TARGET` (default `http://127.0.0.1:8091`). Vite reads the setting from the environment or the repository's `.env` file; it never reaches browser code. The target must serve the console routes. `apps/web/e2e/fixture-console.mjs` is a synthetic console service with deterministic data; `AGENTS_FIXTURE_PORT` changes its port (default 18092). ## Checks diff --git a/apps/web/e2e/access.spec.ts b/apps/web/e2e/access.spec.ts index f22039b3c..d5a1a90ab 100644 --- a/apps/web/e2e/access.spec.ts +++ b/apps/web/e2e/access.spec.ts @@ -1,6 +1,6 @@ import { expect, test, type Page } from "@playwright/test"; -import { expectManagementBoundary, FIXTURE_CORE_KEY, issueKeys, openConsole, resetFixture } from "./console"; +import { expectManagementBoundary, FIXTURE_CORE_KEY, issueKeys, openConsole, recordV1Requests, resetFixture } from "./console"; test.afterEach(async ({ request }) => expectManagementBoundary(request)); @@ -13,6 +13,7 @@ const browserStorage = (page: Page) => page.evaluate(() => JSON.stringify({ ...w test("signs in with the Core key, keeps it out of the browser, and signs out and back in", async ({ page, request }) => { await resetFixture(request, "login"); + await recordV1Requests(page); await page.addInitScript(() => window.localStorage.setItem("agents-core-web.language", "en")); await page.goto("/"); @@ -45,6 +46,7 @@ test("signs in with the Core key, keeps it out of the browser, and signs out and test("opens a fresh install on the Overview's Getting started: a project and its key shown once, then the step is done", async ({ page, request }) => { await resetFixture(request, "login", { fresh: true }); + await recordV1Requests(page); await page.addInitScript(() => window.localStorage.setItem("agents-core-web.language", "en")); await page.goto("/"); await signIn(page, FIXTURE_CORE_KEY); diff --git a/apps/web/e2e/console.ts b/apps/web/e2e/console.ts index 5546bb99b..78000a332 100644 --- a/apps/web/e2e/console.ts +++ b/apps/web/e2e/console.ts @@ -25,9 +25,20 @@ export async function resetFixture(request: APIRequestContext, auth: "login" | " await request.post(`${fixture}/__fixture/reset?auth=${auth}${options.fresh ? "&projects=none" : ""}&sandbox=${options.sandbox ?? "configured"}${options.nodes ? `&nodes=${options.nodes}` : ""}&installation=${options.installation ?? "public"}${options.credentials ? `&credentials=${options.credentials}` : ""}${options.installers ? `&installers=${options.installers}` : ""}${options.nodeArtifacts ? `&artifacts=${options.nodeArtifacts.join(",")}` : ""}`); } +const v1Requests: string[] = []; + +/** Records and aborts every browser request to the application API (/v1), which the console never calls. */ +export async function recordV1Requests(page: Page) { + await page.route((url) => url.pathname === "/v1" || url.pathname.startsWith("/v1/"), (route) => { + v1Requests.push(`${route.request().method()} ${new URL(route.request().url()).pathname}`); + return route.abort(); + }); +} + /** Opens the console already signed in, in English. */ export async function openConsole(page: Page, request: APIRequestContext, hash = "overview", options: FixtureOptions = {}) { await resetFixture(request, "authenticated", options); + await recordV1Requests(page); await page.context().addCookies([{ name: "core_console", value: "fixture-session", url: web }]); await page.addInitScript(() => window.localStorage.setItem("agents-core-web.language", "en")); await page.goto(`/#${hash}`); @@ -75,5 +86,5 @@ export async function writes(request: APIRequestContext): Promise { /** The console never calls /v1 and never sends its own Authorization header. */ export async function expectManagementBoundary(request: APIRequestContext) { const { violations } = await (await request.get(`${fixture}/__fixture/requests`)).json(); - expect(violations).toEqual([]); + expect([...v1Requests.splice(0), ...violations]).toEqual([]); } diff --git a/apps/web/e2e/fixture-console.mjs b/apps/web/e2e/fixture-console.mjs index a7cb44c35..a3de5cafa 100644 --- a/apps/web/e2e/fixture-console.mjs +++ b/apps/web/e2e/fixture-console.mjs @@ -1,9 +1,8 @@ // Browser acceptance fixture: the console service's routes (/console/**) and the // Core management tree it forwards (/core/v1/**: installation, projects, summary, // audit log, metrics, harness default models and /core/v1/sandbox/**), with synthetic, deterministic data and -// in-memory writes. It never serves /v1; any /v1 request, and any -// browser-supplied Authorization header, is recorded so a test can assert that -// the console stays on its management boundary. +// in-memory writes. Any browser-supplied Authorization header is recorded so a +// test can assert that the console stays on its management boundary. import http from "node:http"; import { domainState, domainRoute } from "./data/domain.mjs"; @@ -644,10 +643,6 @@ http.createServer(async (request, response) => { if (url.pathname.startsWith("/__fixture/")) return await fixtureRoute(request, response, url); // The console service serves its distribution manifest to anyone, as nodes download it. if (url.pathname === "/node-install/manifest.json") return send(response, 200, manifest); - if (url.pathname === "/v1" || url.pathname.startsWith("/v1/")) { - state.violations.push(`${request.method} ${url.pathname}`); - return error(response, 404, "The console does not serve /v1."); - } if (request.headers.authorization) state.violations.push(`Authorization header on ${request.method} ${url.pathname}`); if (url.pathname.startsWith("/console/")) return await consoleRoute(request, response, url); const signedIn = state.auth.mode === "authenticated" && request.headers.cookie?.includes(SESSION_COOKIE); diff --git a/apps/web/src/lib/vite-auth.test.ts b/apps/web/src/lib/vite-auth.test.ts deleted file mode 100644 index 5edf311a0..000000000 --- a/apps/web/src/lib/vite-auth.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { chmodSync, mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; -import { homedir } from "node:os"; -import { join } from "node:path"; -import { afterEach, describe, expect, it } from "vitest"; - -import { loadProxyBearerAuth, resolveProxyTokenFile } from "../../vite-auth.ts"; - -const temporaryDirectories: string[] = []; - -function makeTemporaryDirectory(): string { - const parent = join(homedir(), ".oac", "tests"); - mkdirSync(parent, { recursive: true }); - const directory = mkdtempSync(join(parent, "oac-web-auth-")); - temporaryDirectories.push(directory); - return directory; -} - -afterEach(() => { - for (const directory of temporaryDirectories.splice(0)) rmSync(directory, { recursive: true, force: true }); -}); - -describe("Vite OpenAgentCore proxy authentication", () => { - it("falls back to browser-managed authentication when the conventional file is absent", () => { - const homeDir = makeTemporaryDirectory(); - - expect(loadProxyBearerAuth({ homeDir })).toBeUndefined(); - }); - - it("expands the conventional home path and reads a private token file", () => { - const homeDir = makeTemporaryDirectory(); - const stateDirectory = join(homeDir, ".oac", "dev"); - const tokenFile = join(stateDirectory, "web-token"); - mkdirSync(stateDirectory, { recursive: true }); - writeFileSync(tokenFile, "local-tenant-token\n", { mode: 0o600 }); - chmodSync(tokenFile, 0o600); - - expect(resolveProxyTokenFile("~/.oac/dev/web-token", homeDir)).toBe(tokenFile); - expect(loadProxyBearerAuth({ homeDir })).toEqual({ - token: "local-tenant-token", - source: "file", - filePath: tokenFile, - }); - }); - - it("fails closed when an explicitly configured token file cannot be read", () => { - const rootDir = makeTemporaryDirectory(); - expect(() => loadProxyBearerAuth({ tokenFile: "missing-token", rootDir })).toThrow( - "Cannot read OpenAgentCore bearer token file", - ); - }); - - it("rejects empty or group/world-accessible token files", () => { - const rootDir = makeTemporaryDirectory(); - const emptyTokenFile = join(rootDir, "empty-token"); - writeFileSync(emptyTokenFile, "", { mode: 0o600 }); - chmodSync(emptyTokenFile, 0o600); - - expect(() => loadProxyBearerAuth({ tokenFile: emptyTokenFile })).toThrow("is empty"); - - if (process.platform !== "win32") { - const exposedTokenFile = join(rootDir, "exposed-token"); - writeFileSync(exposedTokenFile, "local-tenant-token\n", { mode: 0o644 }); - chmodSync(exposedTokenFile, 0o644); - - expect(() => loadProxyBearerAuth({ tokenFile: exposedTokenFile })).toThrow( - "must not be group/world accessible", - ); - } - }); - - it("rejects ambiguous or malformed server-side credentials", () => { - expect(() => loadProxyBearerAuth({ token: "inline", tokenFile: "/tmp/token" })).toThrow( - "Set only one", - ); - expect(() => loadProxyBearerAuth({ token: "two tokens" })).toThrow("without whitespace"); - }); -}); diff --git a/apps/web/src/lib/vite-config.test.ts b/apps/web/src/lib/vite-config.test.ts index 110440068..ad3d57c71 100644 --- a/apps/web/src/lib/vite-config.test.ts +++ b/apps/web/src/lib/vite-config.test.ts @@ -15,29 +15,24 @@ function configure(env: Record, command: ConfigEnv["command"] = } describe("Web Vite settings boundary", () => { - it("uses current flags and keeps proxy addresses and credentials out of browser definitions", async () => { + it("uses current flags and keeps the proxy address out of browser definitions", async () => { const config = await configure({ OAC_WEB_DEV_PROXY_TARGET: "https://private-host-marker.example", - OAC_WEB_DEV_PROXY_TOKEN: "private-token-marker", OAC_WEB_SELF_HOSTED_SESSIONS: "1", OAC_WEB_OPENAI_HOSTED_SESSIONS: "1", OAC_WEB_ENVIRONMENT_FILES: "1", }); expect(config.define).toEqual({ - __OAC_WEB_DEV_PROXY_AUTH__: "true", __OAC_WEB_SELF_HOSTED_SESSIONS__: "true", __OAC_WEB_OPENAI_HOSTED_SESSIONS__: "true", __OAC_WEB_ENVIRONMENT_FILES__: "true", __OAC_WEB_DOCKER_GUIDE__: "null", __OAC_WEB_DOCKER_BACKEND_GUIDE__: "null", }); + expect(Object.keys(config.server?.proxy ?? {})).toEqual(["/console", "/node-install", "/core/v1"]); expect(config.server?.proxy?.["/core/v1"]).toEqual({ target: "https://private-host-marker.example", changeOrigin: true }); expect(JSON.stringify(config.define)).not.toContain("private-"); }); - it("does not read a development credential file during a production build", async () => { - const config = await configure({ OAC_WEB_DEV_PROXY_TOKEN_FILE: "/missing/private-file-marker" }, "build"); - expect(config.define?.__OAC_WEB_DEV_PROXY_AUTH__).toBe("false"); - expect(JSON.stringify(config.define)).not.toContain("private-file-marker"); - }); + }); diff --git a/apps/web/src/vite-env.d.ts b/apps/web/src/vite-env.d.ts index 44904ac6e..d6371b55b 100644 --- a/apps/web/src/vite-env.d.ts +++ b/apps/web/src/vite-env.d.ts @@ -1,6 +1,5 @@ /// -declare const __OAC_WEB_DEV_PROXY_AUTH__: boolean; declare const __OAC_WEB_SELF_HOSTED_SESSIONS__: boolean; declare const __OAC_WEB_OPENAI_HOSTED_SESSIONS__: boolean; declare const __OAC_WEB_ENVIRONMENT_FILES__: boolean; diff --git a/apps/web/vite-auth.ts b/apps/web/vite-auth.ts deleted file mode 100644 index f6093a136..000000000 --- a/apps/web/vite-auth.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { readFileSync, statSync } from "node:fs"; -import { homedir } from "node:os"; -import { isAbsolute, join, resolve } from "node:path"; - -export const DEFAULT_PROXY_TOKEN_FILE = "~/.oac/dev/web-token"; - -export interface ProxyBearerAuth { - token: string; - source: "environment" | "file"; - filePath?: string; -} - -interface LoadProxyBearerAuthOptions { - token?: string; - tokenFile?: string; - homeDir?: string; - rootDir?: string; -} - -function normalizeToken(value: string, source: string): string { - const token = value.trim(); - if (!token) throw new Error(`${source} is empty.`); - if (/\s/.test(token)) throw new Error(`${source} must contain one bearer token without whitespace.`); - return token; -} - -export function resolveProxyTokenFile( - configuredPath = DEFAULT_PROXY_TOKEN_FILE, - homeDir = homedir(), - rootDir = process.cwd(), -): string { - if (configuredPath === "~") return homeDir; - if (configuredPath.startsWith("~/")) return join(homeDir, configuredPath.slice(2)); - return isAbsolute(configuredPath) ? configuredPath : resolve(rootDir, configuredPath); -} - -export function loadProxyBearerAuth({ - token, - tokenFile, - homeDir = homedir(), - rootDir = process.cwd(), -}: LoadProxyBearerAuthOptions = {}): ProxyBearerAuth | undefined { - const configuredToken = token?.trim(); - const configuredFile = tokenFile?.trim(); - - if (configuredToken && configuredFile) { - throw new Error("Set only one of OAC_WEB_DEV_PROXY_TOKEN or OAC_WEB_DEV_PROXY_TOKEN_FILE."); - } - if (configuredToken) { - return { token: normalizeToken(configuredToken, "OAC_WEB_DEV_PROXY_TOKEN"), source: "environment" }; - } - - const explicitFile = Boolean(configuredFile); - const filePath = resolveProxyTokenFile(configuredFile || DEFAULT_PROXY_TOKEN_FILE, homeDir, rootDir); - let metadata: ReturnType; - try { - metadata = statSync(filePath); - } catch (error) { - if (!explicitFile && (error as NodeJS.ErrnoException).code === "ENOENT") return undefined; - throw new Error(`Cannot read OpenAgentCore bearer token file: ${filePath}`); - } - - if (!metadata.isFile()) throw new Error(`OpenAgentCore bearer token path is not a file: ${filePath}`); - if (process.platform !== "win32" && (metadata.mode & 0o077) !== 0) { - throw new Error(`OpenAgentCore bearer token file must not be group/world accessible: ${filePath}`); - } - - let contents: string; - try { - contents = readFileSync(filePath, "utf8"); - } catch { - throw new Error(`Cannot read OpenAgentCore bearer token file: ${filePath}`); - } - - return { - token: normalizeToken(contents, `OpenAgentCore bearer token file ${filePath}`), - source: "file", - filePath, - }; -} diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 6455e2f91..21dae1a27 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -9,11 +9,10 @@ import { loadLocalDockerBackendGuideProfile, loadLocalDockerGuideProfile, } from "./src/lib/docker-guide-config.ts"; -import { loadProxyBearerAuth } from "./vite-auth.ts"; const repositoryRoot = fileURLToPath(new URL("../..", import.meta.url)); -export default defineConfig(({ command, mode }) => { +export default defineConfig(({ mode }) => { const env = loadEnv(mode, repositoryRoot, ""); const target = env.OAC_WEB_DEV_PROXY_TARGET ?? "http://127.0.0.1:8091"; const selfHostedSessionsEnabled = env.OAC_WEB_SELF_HOSTED_SESSIONS === "1"; @@ -21,17 +20,9 @@ export default defineConfig(({ command, mode }) => { const environmentFilesEnabled = env.OAC_WEB_ENVIRONMENT_FILES === "1"; const localDockerGuide = loadLocalDockerGuideProfile(env); const localDockerBackendGuide = loadLocalDockerBackendGuideProfile(env); - const proxyAuth = command === "serve" && mode !== "test" - ? loadProxyBearerAuth({ - token: env.OAC_WEB_DEV_PROXY_TOKEN, - tokenFile: env.OAC_WEB_DEV_PROXY_TOKEN_FILE, - rootDir: repositoryRoot, - }) - : undefined; return { define: { - __OAC_WEB_DEV_PROXY_AUTH__: JSON.stringify(Boolean(proxyAuth)), __OAC_WEB_SELF_HOSTED_SESSIONS__: JSON.stringify(selfHostedSessionsEnabled), __OAC_WEB_OPENAI_HOSTED_SESSIONS__: JSON.stringify(openAIHostedSessionsEnabled), __OAC_WEB_ENVIRONMENT_FILES__: JSON.stringify(environmentFilesEnabled), @@ -54,16 +45,6 @@ export default defineConfig(({ command, mode }) => { "/console": { target, changeOrigin: true }, "/node-install": { target, changeOrigin: true }, "/core/v1": { target, changeOrigin: true }, - "/v1": { - target, - changeOrigin: true, - configure(proxy) { - if (!proxyAuth) return; - proxy.on("proxyReq", (proxyRequest) => { - proxyRequest.setHeader("authorization", `Bearer ${proxyAuth.token}`); - }); - }, - }, }, }, }; diff --git a/package.json b/package.json index cd3147793..6b9d7c41c 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,7 @@ "dev:web": "pnpm --filter @oac/web dev", "build:web": "pnpm --filter @oac/web build", "typecheck": "pnpm -r --if-present typecheck", - "test": "pnpm --filter @oac/claude-sdk-adapter test && pnpm test:core-doctor && pnpm test:web", - "test:core-doctor": "node --test scripts/core-doctor.test.mjs", + "test": "pnpm --filter @oac/claude-sdk-adapter test && pnpm test:web", "test:web": "pnpm --filter @oac/agents-client test && pnpm --filter @oac/web test", "test:web:acceptance": "playwright test" }, diff --git a/scripts/core-doctor.mjs b/scripts/core-doctor.mjs deleted file mode 100644 index 464889643..000000000 --- a/scripts/core-doctor.mjs +++ /dev/null @@ -1,905 +0,0 @@ -#!/usr/bin/env node - -import { spawn } from "node:child_process"; -import { readFile, stat } from "node:fs/promises"; -import { homedir } from "node:os"; -import { isAbsolute, join, resolve } from "node:path"; -import { pathToFileURL } from "node:url"; -import { parseEnv } from "node:util"; - -export const CORE_DOCTOR_EXIT_CODES = Object.freeze({ - ok: 0, - diagnosticFailure: 1, - usageOrInternalError: 2, -}); - -export const PARSAR_PROTOCOL_BASELINE_REVISION = "0438880ab21aa16d05cb91a4c7f91cc0abc12358"; - -const DEFAULT_TARGET = "http://127.0.0.1:8091"; -const DEFAULT_TOKEN_FILE = "~/.oac/dev/web-token"; -const DEFAULT_PROFILE = "default"; -const DEFAULT_TIMEOUT_MS = 3_000; -const MAX_CONFIG_BYTES = 1024 * 1024; -const MAX_RESPONSE_BYTES = 1024 * 1024; -const MAX_COMMAND_OUTPUT_BYTES = 256 * 1024; -const PROFILE_PATTERN = /^[a-zA-Z0-9._-]{1,64}$/; -const CONFIG_KEYS = new Set([ - "OAC_WEB_DEV_PROXY_TARGET", - "OAC_WEB_DEV_PROXY_TOKEN", - "OAC_WEB_DEV_PROXY_TOKEN_FILE", -]); -const PATH_CONFIG_KEYS = new Set(["OAC_WEB_DEV_PROXY_TOKEN_FILE"]); -const RETIRED_PROXY_SETTINGS = Object.freeze({ - AGENTS_API_PROXY_TARGET: "OAC_WEB_DEV_PROXY_TARGET", - AGENTS_API_PROXY_TOKEN: "OAC_WEB_DEV_PROXY_TOKEN", - AGENTS_API_PROXY_TOKEN_FILE: "OAC_WEB_DEV_PROXY_TOKEN_FILE", -}); -const SAFE_PATH_EXPANSION_KEYS = new Set(["HOME", "OAC_RUNTIME_HOME"]); - -const HELP = `OpenAgentCore Doctor (read-only) - -Usage: - pnpm core:doctor -- [--parsar ] [--profile ] [--timeout-ms ] - -The doctor performs only GET requests. It never creates an Agent, Session, Turn, -or Item, and it never makes a model/provider call. The optional OpenAgentCore checkout is -used only to run the upstream daemon status command. No credential value, response -body, daemon output, or private filesystem path is printed. - -The authenticated read validates only the basic Agent resource envelope. Known -tool variants receive basic field validation; additive JSON fields and unknown -nonempty tool-type discriminants are accepted. A passing result does not prove -that the Web supports those tools or complete Agents API protocol compatibility. - -Pinned Agents API contract: - ${PARSAR_PROTOCOL_BASELINE_REVISION} - -Exit codes: - 0 Core liveness and an authenticated basic Agents API read succeeded. - A daemon may still be unobserved and execution/provider readiness is unknown. - 1 An actionable local configuration or Core connectivity/authentication check failed. - 2 Command usage is invalid or the doctor could not complete safely. -`; - -class CoreDoctorUsageError extends Error {} -class CoreDoctorRetiredSettingsError extends Error {} - -function optionValue(argv, index, option) { - const value = argv[index + 1]; - if (!value || value.startsWith("--")) { - throw new CoreDoctorUsageError(`${option} requires a value`); - } - return value; -} - -export function parseCoreDoctorArgs(argv) { - let parsarPath; - let profile = DEFAULT_PROFILE; - let timeoutMs = DEFAULT_TIMEOUT_MS; - let help = false; - const positional = []; - - for (let index = 0; index < argv.length; index += 1) { - const argument = argv[index]; - if (argument === "--") continue; - if (argument === "-h" || argument === "--help") { - help = true; - continue; - } - if (argument === "--parsar") { - parsarPath = optionValue(argv, index, argument); - index += 1; - continue; - } - if (argument.startsWith("--parsar=")) { - parsarPath = argument.slice("--parsar=".length); - continue; - } - if (argument === "--profile") { - profile = optionValue(argv, index, argument); - index += 1; - continue; - } - if (argument.startsWith("--profile=")) { - profile = argument.slice("--profile=".length); - continue; - } - if (argument === "--timeout-ms") { - timeoutMs = Number(optionValue(argv, index, argument)); - index += 1; - continue; - } - if (argument.startsWith("--timeout-ms=")) { - timeoutMs = Number(argument.slice("--timeout-ms=".length)); - continue; - } - if (argument.startsWith("-")) { - throw new CoreDoctorUsageError("unknown option"); - } - positional.push(argument); - } - - if (positional.length > 1 || (positional.length === 1 && parsarPath)) { - throw new CoreDoctorUsageError("provide at most one OpenAgentCore checkout"); - } - if (positional.length === 1) parsarPath = positional[0]; - if (typeof parsarPath === "string" && parsarPath.trim() === "") { - throw new CoreDoctorUsageError("OpenAgentCore checkout cannot be empty"); - } - if (!PROFILE_PATTERN.test(profile)) { - throw new CoreDoctorUsageError("invalid daemon profile name"); - } - if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 60_000) { - throw new CoreDoctorUsageError("timeout must be between 100 and 60000 milliseconds"); - } - - return { help, parsarPath, profile, timeoutMs }; -} - -function resolveConfiguredPath(configuredPath, homeDir, rootDir) { - if (configuredPath === "~") return homeDir; - if (configuredPath.startsWith("~/")) return join(homeDir, configuredPath.slice(2)); - return isAbsolute(configuredPath) ? configuredPath : resolve(rootDir, configuredPath); -} - -async function readSmallText(path, maximumBytes = MAX_CONFIG_BYTES) { - const metadata = await stat(path); - if (!metadata.isFile() || metadata.size > maximumBytes) throw new Error("unsafe file"); - return readFile(path, "utf8"); -} - -function expandDotEnvValue(value, processEnv, runningParsed) { - const environment = { ...runningParsed, ...processEnv }; - const expressionPattern = /(? !SAFE_PATH_EXPANSION_KEYS.has(name))) { - throw new CoreDoctorUsageError("unsafe variable expansion in local Core configuration"); - } - const safeEnvironment = Object.fromEntries([...SAFE_PATH_EXPANSION_KEYS].flatMap((name) => - typeof env[name] === "string" ? [[name, env[name]]] : [], - )); - return expandDotEnvValue(value, safeEnvironment, {}).replace(/\\\$/g, "$"); -} - -function rejectRetiredProxySettings(env, parsed = {}) { - // Diagnostics contain only allowlisted names, never configuration values. - const retired = Object.entries(RETIRED_PROXY_SETTINGS).filter(([name]) => Object.hasOwn(env, name) || Object.hasOwn(parsed, name)); - if (retired.length) { - throw new CoreDoctorRetiredSettingsError(`Retired Web settings: ${retired.map(([name, replacement]) => `${name} is no longer supported; use ${replacement}`).join(". ")}.`); - } -} - -export async function loadCoreDoctorConfig({ env, cwd }) { - rejectRetiredProxySettings(env); - const parsed = {}; - const dotenvFiles = [".env", ".env.local", ".env.development", ".env.development.local"]; - - for (const name of dotenvFiles) { - try { - Object.assign(parsed, parseEnv(await readSmallText(join(cwd, name)))); - } catch (error) { - if (error?.code !== "ENOENT") { - rejectRetiredProxySettings(env, parsed); - throw new CoreDoctorUsageError("local environment file is unreadable or unsafe"); - } - } - } - - rejectRetiredProxySettings(env, parsed); - - const loaded = {}; - for (const key of CONFIG_KEYS) { - if (Object.hasOwn(env, key)) loaded[key] = String(env[key] ?? ""); - else if (Object.hasOwn(parsed, key)) loaded[key] = expandConfiguredValue(key, parsed[key], env); - } - return loaded; -} - -function parseCoreTarget(value) { - let target; - try { - target = new URL(value.trim()); - } catch { - throw new CoreDoctorUsageError("Core proxy target is not a valid URL"); - } - - if ( - (target.protocol !== "http:" && target.protocol !== "https:") || - !target.hostname || - target.username || - target.password || - target.search || - target.hash || - (target.pathname !== "" && target.pathname !== "/") - ) { - throw new CoreDoctorUsageError("Core proxy target must be a credential-free HTTP(S) origin"); - } - - const hostname = target.hostname.toLowerCase(); - const loopback = - hostname === "localhost" || - hostname.endsWith(".localhost") || - hostname === "[::1]" || - /^127(?:\.\d{1,3}){3}$/.test(hostname); - if (target.protocol === "http:" && !loopback) { - throw new CoreDoctorUsageError("remote Core proxy targets must use HTTPS"); - } - - return { - displayOrigin: loopback ? target.origin : "remote HTTPS origin", - healthUrl: new URL("/healthz", target.origin).href, - agentsUrl: new URL("/v1/agents?limit=1", target.origin).href, - }; -} - -function createReport() { - const checks = []; - return { - add(level, layer, message) { - checks.push({ level, layer, message }); - }, - render(exitCode) { - const lines = [ - "OpenAgentCore Doctor (read-only)", - `Agents API protocol baseline: ${PARSAR_PROTOCOL_BASELINE_REVISION}`, - "", - ]; - for (const check of checks) lines.push(`[${check.level}] ${check.layer}: ${check.message}`); - lines.push(""); - if (exitCode === CORE_DOCTOR_EXIT_CODES.ok) { - lines.push( - "Result: Core API checks passed for the basic Agent resource envelope; tool/Web compatibility, full protocol compatibility, and execution readiness remain unknown.", - ); - } else if (exitCode === CORE_DOCTOR_EXIT_CODES.diagnosticFailure) { - lines.push("Result: actionable local configuration or Core check failures were found."); - } else { - lines.push("Result: the doctor could not complete because invocation or configuration is invalid."); - } - return `${lines.join("\n")}\n`; - }, - checks, - }; -} - -function normalizeToken(rawToken) { - const token = rawToken.trim(); - if (!token || token.length > 64 * 1024 || /\s/.test(token)) return undefined; - return token; -} - -async function inspectPrivateFile(path, label, { platform, report }) { - let metadata; - try { - metadata = await stat(path); - } catch (error) { - report.add("FAIL", label, "file is missing or unreadable."); - return { exitCode: CORE_DOCTOR_EXIT_CODES.diagnosticFailure, value: undefined }; - } - if (!metadata.isFile() || metadata.size > MAX_CONFIG_BYTES) { - report.add("FAIL", label, "path is not a bounded regular file."); - return { exitCode: CORE_DOCTOR_EXIT_CODES.diagnosticFailure, value: undefined }; - } - if (platform === "win32") { - report.add("FAIL", label, "owner-only permissions cannot be verified on this platform; file was not read."); - return { exitCode: CORE_DOCTOR_EXIT_CODES.diagnosticFailure, value: undefined }; - } - if ((metadata.mode & 0o077) !== 0) { - report.add("FAIL", label, "file is group/world accessible; use owner-only permissions."); - return { exitCode: CORE_DOCTOR_EXIT_CODES.diagnosticFailure, value: undefined }; - } - try { - const value = await readFile(path, "utf8"); - report.add("PASS", label, "file is present with private permissions."); - return { exitCode: CORE_DOCTOR_EXIT_CODES.ok, value }; - } catch { - report.add("FAIL", label, "file is missing or unreadable."); - return { exitCode: CORE_DOCTOR_EXIT_CODES.diagnosticFailure, value: undefined }; - } -} - -export async function inspectCoreCredentials({ config, cwd, homeDir, platform, report }) { - const configuredToken = config.OAC_WEB_DEV_PROXY_TOKEN?.trim() ?? ""; - const configuredTokenFile = config.OAC_WEB_DEV_PROXY_TOKEN_FILE?.trim() ?? ""; - let exitCode = CORE_DOCTOR_EXIT_CODES.ok; - let token; - let tokenFile; - - if (configuredToken && configuredTokenFile) { - report.add("FAIL", "Caller token", "choose only one server-side token source."); - return { exitCode: CORE_DOCTOR_EXIT_CODES.diagnosticFailure, token: undefined }; - } - - if (configuredToken) { - token = normalizeToken(configuredToken); - if (token) report.add("PASS", "Caller token", "server-process token is configured in memory."); - else { - report.add("FAIL", "Caller token", "server-process token is empty or malformed."); - exitCode = CORE_DOCTOR_EXIT_CODES.diagnosticFailure; - } - } else { - tokenFile = resolveConfiguredPath(configuredTokenFile || DEFAULT_TOKEN_FILE, homeDir, cwd); - const tokenInspection = await inspectPrivateFile(tokenFile, "Caller token", { platform, report }); - exitCode = Math.max(exitCode, tokenInspection.exitCode); - if (tokenInspection.value !== undefined) { - token = normalizeToken(tokenInspection.value); - if (!token) { - report.add("FAIL", "Caller token", "file does not contain one valid bearer token."); - exitCode = CORE_DOCTOR_EXIT_CODES.diagnosticFailure; - } - } - } - - return { exitCode, token }; -} - -async function readJsonResponse(response) { - if (!response.body) throw new Error("missing body"); - const declaredLength = Number(response.headers.get("content-length")); - if (Number.isFinite(declaredLength) && declaredLength > MAX_RESPONSE_BYTES) { - await response.body.cancel(); - throw new Error("response too large"); - } - - const reader = response.body.getReader(); - const chunks = []; - let total = 0; - try { - while (true) { - const { done, value } = await reader.read(); - if (done) break; - total += value.byteLength; - if (total > MAX_RESPONSE_BYTES) throw new Error("response too large"); - chunks.push(value); - } - } catch (error) { - await reader.cancel().catch(() => {}); - throw error; - } - return JSON.parse(Buffer.concat(chunks).toString("utf8")); -} - -async function discardResponse(response) { - await response.body?.cancel().catch(() => {}); -} - -async function fetchOnce(fetchImpl, url, init, timeoutMs) { - return fetchImpl(url, { ...init, signal: AbortSignal.timeout(timeoutMs), redirect: "error" }); -} - -function isRecord(value) { - return value !== null && typeof value === "object" && !Array.isArray(value); -} - -function hasOwn(value, key) { - return Object.prototype.hasOwnProperty.call(value, key); -} - -function isNonEmptyString(value) { - return typeof value === "string" && value.length > 0; -} - -function isNullableString(value) { - return value === null || typeof value === "string"; -} - -function isStringRecord(value) { - return isRecord(value) && Object.values(value).every((entry) => typeof entry === "string"); -} - -function isCredentialFreeHTTPUrl(value) { - if ( - !isNonEmptyString(value) || - value.trim() !== value || - value.includes("?") || - value.includes("#") - ) { - return false; - } - try { - const url = new URL(value); - return ( - (url.protocol === "http:" || url.protocol === "https:") && - Boolean(url.hostname) && - !url.username && - !url.password && - !url.search && - !url.hash - ); - } catch { - return false; - } -} - -function isCanonicalSavedMCPTool(value) { - const transport = value.transport; - return ( - hasOwn(value, "server_label") && - isNonEmptyString(value.server_label) && - value.server_label.trim() !== "" && - hasOwn(value, "transport") && - isRecord(transport) && - transport.type === "http" && - hasOwn(transport, "server_url") && - isCredentialFreeHTTPUrl(transport.server_url) && - (!hasOwn(transport, "headers") || - (isStringRecord(transport.headers) && Object.keys(transport.headers).length === 0)) && - hasOwn(value, "allowed_tools") && - (value.allowed_tools === null || - (Array.isArray(value.allowed_tools) && value.allowed_tools.every(isNonEmptyString))) && - value.connection_origin === "service" && - hasOwn(value, "credential_id") && - (value.credential_id === null || isNonEmptyString(value.credential_id)) && - hasOwn(value, "request_metadata") && - isRecord(value.request_metadata) && - Object.keys(value.request_metadata).length === 0 && - hasOwn(value, "required") && - value.required === false - ); -} - -function isCanonicalMultiAgent(value) { - if ( - !isRecord(value) || - !hasOwn(value, "enabled") || - typeof value.enabled !== "boolean" || - !hasOwn(value, "max_concurrent_subagents") - ) { - return false; - } - if (!value.enabled) return value.max_concurrent_subagents === null; - return ( - Number.isSafeInteger(value.max_concurrent_subagents) && - value.max_concurrent_subagents > 0 && - value.max_concurrent_subagents <= 4_294_967_295 - ); -} - -const REASONING_EFFORTS = new Set(["none", "minimal", "low", "medium", "high", "xhigh", "max"]); -const REASONING_SUMMARIES = new Set(["concise", "detailed", "auto"]); -const SERVICE_TIERS = new Set(["auto", "default", "flex", "priority", "fast"]); -const TEXT_VERBOSITIES = new Set(["low", "medium", "high"]); - -function isOptionalNullableEnum(value, key, allowed) { - return !hasOwn(value, key) || value[key] === null || allowed.has(value[key]); -} - -function isCanonicalReasoning(value) { - return ( - isRecord(value) && - isOptionalNullableEnum(value, "effort", REASONING_EFFORTS) && - isOptionalNullableEnum(value, "summary", REASONING_SUMMARIES) - ); -} - -function isCanonicalTextFormat(value) { - if (!isRecord(value) || !hasOwn(value, "type")) return false; - if (value.type === "text") return !hasOwn(value, "schema"); - return value.type === "json_schema" && hasOwn(value, "schema") && isRecord(value.schema); -} - -function isCanonicalText(value) { - return ( - isRecord(value) && - hasOwn(value, "format") && - isCanonicalTextFormat(value.format) && - hasOwn(value, "verbosity") && - TEXT_VERBOSITIES.has(value.verbosity) - ); -} - -function isBasicSavedAgentToolEnvelope(value) { - if (!isRecord(value) || !isNonEmptyString(value.type)) return false; - if (value.type === "tool_search") return true; - if (value.type === "programmatic_tool_calling") { - return hasOwn(value, "enabled") && typeof value.enabled === "boolean"; - } - if (value.type === "function") { - return ( - hasOwn(value, "name") && - typeof value.name === "string" && - hasOwn(value, "description") && - typeof value.description === "string" && - hasOwn(value, "parameters") && - isRecord(value.parameters) && - hasOwn(value, "defer_loading") && - typeof value.defer_loading === "boolean" - ); - } - if (value.type === "mcp") return isCanonicalSavedMCPTool(value); - return true; -} - -function isCanonicalSavedAgent(value) { - const requiredFields = [ - "id", - "object", - "model", - "name", - "instructions", - "metadata", - "multi_agent", - "reasoning", - "service_tier", - "text", - "tools", - "created_at", - "updated_at", - ]; - return ( - isRecord(value) && - requiredFields.every((field) => hasOwn(value, field)) && - isNonEmptyString(value.id) && - value.object === "agent" && - typeof value.model === "string" && - isNullableString(value.name) && - isNullableString(value.instructions) && - isStringRecord(value.metadata) && - isCanonicalMultiAgent(value.multi_agent) && - isCanonicalReasoning(value.reasoning) && - SERVICE_TIERS.has(value.service_tier) && - isCanonicalText(value.text) && - Array.isArray(value.tools) && - value.tools.every(isBasicSavedAgentToolEnvelope) && - Number.isSafeInteger(value.created_at) && - value.created_at >= 0 && - Number.isSafeInteger(value.updated_at) && - value.updated_at >= 0 - ); -} - -function isCanonicalSavedAgentList(value, limit) { - if ( - !isRecord(value) || - value.object !== "list" || - !hasOwn(value, "data") || - !Array.isArray(value.data) || - value.data.length > limit || - !value.data.every(isCanonicalSavedAgent) || - !hasOwn(value, "has_more") || - typeof value.has_more !== "boolean" || - !hasOwn(value, "first_id") || - !hasOwn(value, "last_id") || - !(value.first_id === null || isNonEmptyString(value.first_id)) || - !(value.last_id === null || isNonEmptyString(value.last_id)) - ) { - return false; - } - - if (value.data.length === 0) { - return value.has_more === false && value.first_id === null && value.last_id === null; - } - return value.first_id === value.data[0].id && value.last_id === value.data.at(-1).id; -} - -export async function probeCore({ target, token, timeoutMs, fetchImpl, report }) { - let exitCode = CORE_DOCTOR_EXIT_CODES.ok; - let reachable = false; - - try { - const response = await fetchOnce(fetchImpl, target.healthUrl, { method: "GET" }, timeoutMs); - reachable = true; - if (response.status === 200) { - let health; - try { - health = await readJsonResponse(response); - } catch { - health = undefined; - } - if (health?.status === "ok") report.add("PASS", "Core liveness", "Core health endpoint responded ok."); - else { - report.add("FAIL", "Core liveness", "Core health response did not match the expected contract."); - exitCode = CORE_DOCTOR_EXIT_CODES.diagnosticFailure; - } - } else { - await discardResponse(response); - report.add("FAIL", "Core liveness", `Core health endpoint returned HTTP ${response.status}.`); - exitCode = CORE_DOCTOR_EXIT_CODES.diagnosticFailure; - } - } catch { - report.add("FAIL", "Core liveness", "Core is unreachable or the liveness request timed out."); - exitCode = CORE_DOCTOR_EXIT_CODES.diagnosticFailure; - } - - if (!token) { - report.add("WARN", "Core API", "authenticated read skipped because no valid caller token is available."); - return { exitCode: Math.max(exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure) }; - } - if (!reachable) { - report.add("WARN", "Core API", "authenticated read skipped because Core was unreachable."); - return { exitCode }; - } - - try { - const response = await fetchOnce( - fetchImpl, - target.agentsUrl, - { - method: "GET", - headers: { - accept: "application/json", - authorization: `Bearer ${token}`, - "openai-beta": "agents=v1", - }, - }, - timeoutMs, - ); - - if (response.status === 200) { - let payload; - try { - payload = await readJsonResponse(response); - } catch { - payload = undefined; - } - if (isCanonicalSavedAgentList(payload, 1)) { - report.add( - "PASS", - "Core API", - "Core API authenticated; basic Agent resource envelope parsed.", - ); - } else { - report.add("FAIL", "Core API", "authenticated response did not match the expected list contract."); - exitCode = CORE_DOCTOR_EXIT_CODES.diagnosticFailure; - } - } else { - await discardResponse(response); - if (response.status === 401 || response.status === 403) { - report.add("FAIL", "Core API", `authentication was rejected (HTTP ${response.status}).`); - } else if (response.status === 400) { - report.add("FAIL", "Core API", "Agents protocol headers were rejected (HTTP 400)."); - } else if (response.status === 404 || response.status === 405) { - report.add("FAIL", "Core API", `basic Agents read is unavailable (HTTP ${response.status}).`); - } else { - report.add("FAIL", "Core API", `basic Agents read failed (HTTP ${response.status}).`); - } - exitCode = CORE_DOCTOR_EXIT_CODES.diagnosticFailure; - } - } catch { - report.add("FAIL", "Core API", "authenticated read failed or timed out."); - exitCode = CORE_DOCTOR_EXIT_CODES.diagnosticFailure; - } - - return { exitCode }; -} - -function minimalCommandEnvironment(env) { - const allowed = ["GOCACHE", "GOENV", "GOMODCACHE", "GOPATH", "GOROOT", "HOME", "OAC_RUNTIME_HOME", "PATH", "TMPDIR"]; - return Object.fromEntries(allowed.flatMap((name) => - typeof env[name] === "string" && env[name] !== "" ? [[name, env[name]]] : [], - )); -} - -async function spawnCommand({ command, args, cwd, env, timeoutMs }) { - return new Promise((resolveResult) => { - const child = spawn(command, args, { - cwd, - env, - shell: false, - stdio: ["ignore", "pipe", "ignore"], - windowsHide: true, - }); - const stdout = []; - let outputBytes = 0; - let settled = false; - const finish = (result) => { - if (settled) return; - settled = true; - clearTimeout(timer); - resolveResult({ - stdout: Buffer.concat(stdout).toString("utf8"), - ...result, - }); - }; - const collect = (chunks) => (chunk) => { - outputBytes += chunk.byteLength; - if (outputBytes > MAX_COMMAND_OUTPUT_BYTES) { - child.kill("SIGTERM"); - finish({ code: null, outputLimit: true }); - } else { - chunks.push(chunk); - } - }; - child.stdout.on("data", collect(stdout)); - child.once("error", (error) => finish({ code: null, errorCode: error.code })); - child.once("close", (code) => finish({ code })); - const timer = setTimeout(() => { - child.kill("SIGTERM"); - finish({ code: null, timedOut: true }); - }, timeoutMs); - }); -} - -export function parseDaemonStatus(source) { - if (/^paired\s*:\s*ERROR\b/im.test(source) || /^background\s*:\s*ERROR\b/im.test(source)) return "unknown"; - const paired = /^paired\s*:\s*yes\s*$/im.test(source); - const unpaired = /^paired\s*:\s*no legacy profile\b/im.test(source); - const background = /^background\s*:\s*pidfile present\b/im.test(source); - const absent = /^background\s*:\s*not started\b/im.test(source); - if (paired && background) return "paired-background-observed"; - if (paired && absent) return "paired-process-not-observed"; - if (unpaired && (absent || !background)) return "not-observed"; - return "unknown"; -} - -async function validateParsarCheckout(parsarPath) { - try { - const root = resolve(parsarPath); - const [rootMetadata, moduleMetadata, commandMetadata] = await Promise.all([ - stat(root), - stat(join(root, "go.mod")), - stat(join(root, "apps/daemon/cmd/oac-daemon/main.go")), - ]); - if (!rootMetadata.isDirectory() || !moduleMetadata.isFile() || !commandMetadata.isFile()) return undefined; - return root; - } catch { - return undefined; - } -} - -export async function inspectDaemon({ parsarPath, profile, timeoutMs, env, runCommand, report }) { - let command; - let args; - let cwd; - - if (parsarPath) { - cwd = await validateParsarCheckout(parsarPath); - if (!cwd) { - report.add("FAIL", "Daemon", "the supplied OpenAgentCore checkout is invalid or unreadable."); - return { exitCode: CORE_DOCTOR_EXIT_CODES.usageOrInternalError }; - } - command = "go"; - args = ["run", "./apps/daemon/cmd/oac-daemon", "status", "--profile", profile]; - } else { - command = "oac-daemon"; - args = ["status", "--profile", profile]; - } - - const result = await runCommand({ - command, - args, - cwd, - env: minimalCommandEnvironment(env), - timeoutMs: parsarPath ? Math.max(timeoutMs, 15_000) : timeoutMs, - }); - - if (result.code !== 0 || result.timedOut || result.outputLimit || result.errorCode) { - report.add("WARN", "Daemon", "daemon profile/process status was not observed."); - return { exitCode: CORE_DOCTOR_EXIT_CODES.ok }; - } - - const status = parseDaemonStatus(result.stdout); - if (status === "paired-background-observed") { - report.add("OBSERVED", "Daemon", "paired profile and pid file reported; process and connection remain unknown."); - } else if (status === "paired-process-not-observed") { - report.add("WARN", "Daemon", "profile is paired, but a daemon process was not observed."); - } else if (status === "not-observed") { - report.add("WARN", "Daemon", "daemon profile/process was not observed."); - } else { - report.add("WARN", "Daemon", "upstream status was inconclusive; daemon connection is unknown."); - } - return { exitCode: CORE_DOCTOR_EXIT_CODES.ok }; -} - -export async function runCoreDoctor({ - argv = process.argv.slice(2), - env = process.env, - cwd = process.cwd(), - homeDir = homedir(), - platform = process.platform, - fetchImpl = globalThis.fetch, - runCommand = spawnCommand, - stdout = process.stdout, - stderr = process.stderr, -} = {}) { - let options; - try { - options = parseCoreDoctorArgs(argv); - } catch { - stderr.write("Invalid Core Doctor options. Run `pnpm core:doctor -- --help`.\n"); - return { exitCode: CORE_DOCTOR_EXIT_CODES.usageOrInternalError, checks: [] }; - } - if (options.help) { - stdout.write(HELP); - return { exitCode: CORE_DOCTOR_EXIT_CODES.ok, checks: [] }; - } - - const report = createReport(); - let exitCode = CORE_DOCTOR_EXIT_CODES.ok; - let config; - try { - config = await loadCoreDoctorConfig({ env, cwd }); - } catch (error) { - report.add("FAIL", "Configuration", error instanceof CoreDoctorRetiredSettingsError - ? error.message : "local environment configuration is unreadable or unsafe."); - const exitCode = CORE_DOCTOR_EXIT_CODES.diagnosticFailure; - stdout.write(report.render(exitCode)); - return { exitCode, checks: report.checks }; - } - - let target; - try { - target = parseCoreTarget(config.OAC_WEB_DEV_PROXY_TARGET || DEFAULT_TARGET); - report.add("PASS", "Configuration", `proxy target is configured (${target.displayOrigin}).`); - } catch { - report.add("FAIL", "Configuration", "proxy target must be credential-free HTTPS or a loopback HTTP origin."); - const exitCode = CORE_DOCTOR_EXIT_CODES.diagnosticFailure; - stdout.write(report.render(exitCode)); - return { exitCode, checks: report.checks }; - } - - const credentials = await inspectCoreCredentials({ config, cwd, homeDir, platform, report }); - exitCode = Math.max(exitCode, credentials.exitCode); - - const core = await probeCore({ - target, - token: credentials.token, - timeoutMs: options.timeoutMs, - fetchImpl, - report, - }); - exitCode = Math.max(exitCode, core.exitCode); - - const daemon = await inspectDaemon({ - parsarPath: options.parsarPath, - profile: options.profile, - timeoutMs: options.timeoutMs, - env, - runCommand, - report, - }); - exitCode = Math.max(exitCode, daemon.exitCode); - report.add("UNKNOWN", "Execution", "executor, model, and provider readiness were not verified."); - - stdout.write(report.render(exitCode)); - return { exitCode, checks: report.checks }; -} - -const invokedUrl = process.argv[1] ? pathToFileURL(resolve(process.argv[1])).href : ""; -if (import.meta.url === invokedUrl) { - runCoreDoctor() - .then(({ exitCode }) => { - process.exitCode = exitCode; - }) - .catch(() => { - process.stderr.write("Core Doctor could not complete safely.\n"); - process.exitCode = CORE_DOCTOR_EXIT_CODES.usageOrInternalError; - }); -} diff --git a/scripts/core-doctor.test.mjs b/scripts/core-doctor.test.mjs deleted file mode 100644 index 4bfb0f62f..000000000 --- a/scripts/core-doctor.test.mjs +++ /dev/null @@ -1,838 +0,0 @@ -import assert from "node:assert/strict"; -import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; -import { homedir } from "node:os"; -import { join } from "node:path"; -import test from "node:test"; - -import { - CORE_DOCTOR_EXIT_CODES, - PARSAR_PROTOCOL_BASELINE_REVISION, - parseDaemonStatus, - runCoreDoctor, -} from "./core-doctor.mjs"; - -const testRoot = join(homedir(), ".oac", "tests"); -await mkdir(testRoot, { recursive: true }); - -const fixtureRoot = new URL("./fixtures/core-doctor/", import.meta.url); -const fixtureToken = "fixture-bearer"; -const fixtureTarget = "https://core.fixture.invalid"; - -async function fixture(name) { - return readFile(new URL(name, fixtureRoot), "utf8"); -} - -function captureStream() { - let value = ""; - return { - stream: { - write(chunk) { - value += String(chunk); - return true; - }, - }, - value: () => value, - }; -} - -async function createLocalState(t, { token = fixtureToken } = {}) { - const root = await mkdtemp(join(testRoot, "agents-core-doctor-")); - t.after(() => rm(root, { recursive: true, force: true })); - const homeDir = join(root, "home"); - const stateDir = join(homeDir, ".oac", "dev"); - await mkdir(stateDir, { recursive: true, mode: 0o700 }); - await writeFile(join(stateDir, "web-token"), token, { mode: 0o600 }); - await chmod(join(stateDir, "web-token"), 0o600); - return { root, homeDir, stateDir }; -} - -async function successfulFetchRecorder({ apiStatus = 200, apiBody, healthStatus = 200, healthBody } = {}) { - const requests = []; - const agentsBody = apiBody ?? await fixture("agents-list.json"); - const fetchImpl = async (url, init = {}) => { - const parsed = new URL(url); - requests.push({ - url: parsed, - method: init.method, - headers: new Headers(init.headers), - redirect: init.redirect, - }); - if (parsed.pathname === "/healthz") { - return new Response(healthBody ?? JSON.stringify({ status: "ok" }), { - status: healthStatus, - headers: { "content-type": "application/json" }, - }); - } - return new Response(agentsBody, { - status: apiStatus, - headers: { "content-type": "application/json" }, - }); - }; - return { fetchImpl, requests }; -} - -function canonicalAgent(overrides = {}) { - return { - id: "agent_fixture", - object: "agent", - model: "fixture/model", - name: "Fixture Agent", - instructions: null, - metadata: { fixture: "safe" }, - multi_agent: { enabled: false, max_concurrent_subagents: null }, - reasoning: {}, - service_tier: "auto", - text: { format: { type: "text" }, verbosity: "medium" }, - tools: [], - created_at: 1_789_438_200, - updated_at: 1_789_438_800, - ...overrides, - }; -} - -function canonicalMCPTool(overrides = {}) { - return { - type: "mcp", - server_label: "records", - transport: { type: "http", server_url: "https://mcp.fixture.invalid/tools", headers: {} }, - allowed_tools: null, - connection_origin: "service", - credential_id: null, - request_metadata: {}, - required: false, - ...overrides, - }; -} - -function canonicalAgentPage(agent = canonicalAgent(), overrides = {}) { - const cursor = agent && typeof agent === "object" ? agent.id : null; - return { - object: "list", - data: [agent], - first_id: cursor, - last_id: cursor, - has_more: false, - ...overrides, - }; -} - -async function runScenario({ - argv = [], - env = {}, - cwd, - homeDir, - platform = "darwin", - fetchImpl, - runCommand = async () => ({ code: 0, stdout: await fixture("daemon-status-absent.txt"), stderr: "" }), -} = {}) { - const stdout = captureStream(); - const stderr = captureStream(); - const result = await runCoreDoctor({ - argv, - env, - cwd, - homeDir, - platform, - fetchImpl, - runCommand, - stdout: stdout.stream, - stderr: stderr.stream, - }); - return { result, stdout: stdout.value(), stderr: stderr.value() }; -} - -test("documents the read-only command and exit-code contract", async () => { - const result = await runScenario({ - argv: ["--help"], - env: {}, - cwd: process.cwd(), - homeDir: testRoot, - fetchImpl: async () => assert.fail("help must not make a request"), - runCommand: async () => assert.fail("help must not inspect a daemon"), - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.ok); - assert.match(result.stdout, /performs only GET requests/); - assert.match(result.stdout, new RegExp(PARSAR_PROTOCOL_BASELINE_REVISION)); - assert.match(result.stdout, /additive JSON fields and unknown\nnonempty tool-type discriminants are accepted/); - assert.match(result.stdout, /does not prove\s+that the Web supports those tools/); - assert.match(result.stdout, /Exit codes:\n 0[\s\S]*\n 1[\s\S]*\n 2/); - assert.equal(result.stderr, ""); -}); - -test("authenticates a basic GET without leaking the token or daemon output", async (t) => { - const state = await createLocalState(t); - const { fetchImpl, requests } = await successfulFetchRecorder(); - const daemonOutput = await fixture("daemon-status-paired.txt"); - let commandCall; - const result = await runScenario({ - env: { - OAC_WEB_DEV_PROXY_TARGET: fixtureTarget, - OPENAI_API_KEY: "provider-secret-marker", - PATH: "/synthetic/bin", - HOME: state.homeDir, - }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl, - runCommand: async (call) => { - commandCall = call; - return { code: 0, stdout: daemonOutput, stderr: "runner_credential=fixture-super-secret-token" }; - }, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.ok); - assert.match(result.stdout, new RegExp(PARSAR_PROTOCOL_BASELINE_REVISION)); - assert.match(result.stdout, /Core API authenticated; basic Agent resource envelope parsed/); - assert.match(result.stdout, /tool\/Web compatibility, full protocol compatibility/); - assert.match(result.stdout, /paired profile and pid file reported; process and connection remain unknown/); - assert.doesNotMatch(result.stdout, /\[PASS\] Daemon/); - assert.match(result.stdout, /executor, model, and provider readiness were not verified/); - assert.doesNotMatch(result.stdout, new RegExp(fixtureToken)); - assert.doesNotMatch(result.stdout, /synthetic\/private|synthetic-runtime|synthetic-host/); - assert.equal(result.stderr, ""); - assert.deepEqual(requests.map(({ method }) => method), ["GET", "GET"]); - assert.deepEqual(requests.map(({ url }) => `${url.pathname}${url.search}`), ["/healthz", "/v1/agents?limit=1"]); - assert.equal(requests[0].headers.has("authorization"), false); - assert.equal(requests[1].headers.get("authorization"), `Bearer ${fixtureToken}`); - assert.equal(requests[1].headers.get("openai-beta"), "agents=v1"); - assert.equal(requests[1].redirect, "error"); - assert.deepEqual(commandCall.args, ["status", "--profile", "default"]); - assert.equal(commandCall.env.OPENAI_API_KEY, undefined); -}); - -test("reports missing conventional credential files and skips the authenticated read", async (t) => { - const root = await mkdtemp(join(testRoot, "agents-core-doctor-missing-")); - t.after(() => rm(root, { recursive: true, force: true })); - const { fetchImpl, requests } = await successfulFetchRecorder(); - const result = await runScenario({ - env: { OAC_WEB_DEV_PROXY_TARGET: fixtureTarget }, - cwd: root, - homeDir: root, - fetchImpl, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.match(result.stdout, /Caller token: file is missing or unreadable/); - assert.match(result.stdout, /authenticated read skipped because no valid caller token/); - assert.equal(requests.length, 1); -}); - -test("refuses to read a group/world-accessible token file", async (t) => { - const state = await createLocalState(t); - await chmod(join(state.stateDir, "web-token"), 0o644); - const { fetchImpl, requests } = await successfulFetchRecorder(); - const result = await runScenario({ - env: { OAC_WEB_DEV_PROXY_TARGET: fixtureTarget }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.match(result.stdout, /Caller token: file is group\/world accessible/); - assert.equal(requests.length, 1); - assert.doesNotMatch(result.stdout, new RegExp(state.stateDir.replaceAll("/", "\\/"))); -}); - -test("accepts an issued caller token using the authenticated GET probe", async (t) => { - const root = await mkdtemp(join(testRoot, "agents-core-doctor-inline-")); - t.after(() => rm(root, { recursive: true, force: true })); - const { fetchImpl, requests } = await successfulFetchRecorder(); - const result = await runScenario({ - env: { - OAC_WEB_DEV_PROXY_TARGET: fixtureTarget, - OAC_WEB_DEV_PROXY_TOKEN: fixtureToken, - }, - cwd: root, - homeDir: root, - fetchImpl, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.ok); - assert.match(result.stdout, /Core API authenticated; basic Agent resource envelope parsed/); - assert.equal(requests.length, 2); -}); - -test("treats conflicting server-side token sources as an actionable configuration failure", async (t) => { - const state = await createLocalState(t); - const { fetchImpl } = await successfulFetchRecorder(); - const result = await runScenario({ - env: { - OAC_WEB_DEV_PROXY_TARGET: fixtureTarget, - OAC_WEB_DEV_PROXY_TOKEN: fixtureToken, - OAC_WEB_DEV_PROXY_TOKEN_FILE: join(state.stateDir, "web-token"), - }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.match(result.stdout, /choose only one server-side token source/); -}); - -test("distinguishes an unreachable Core without retrying", async (t) => { - const state = await createLocalState(t); - let calls = 0; - const result = await runScenario({ - env: { OAC_WEB_DEV_PROXY_TARGET: "http://127.0.0.1:1" }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl: async () => { - calls += 1; - throw new TypeError("synthetic connection refused"); - }, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.match(result.stdout, /Core is unreachable or the liveness request timed out/); - assert.match(result.stdout, /authenticated read skipped because Core was unreachable/); - assert.equal(calls, 1); -}); - -test("distinguishes a 401 from liveness and discards the response body", async (t) => { - const state = await createLocalState(t); - const { fetchImpl } = await successfulFetchRecorder({ - apiStatus: 401, - apiBody: JSON.stringify({ error: { message: "session-private-marker", code: "invalid_api_key" } }), - }); - const result = await runScenario({ - env: { OAC_WEB_DEV_PROXY_TARGET: fixtureTarget }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.match(result.stdout, /authentication was rejected \(HTTP 401\)/); - assert.doesNotMatch(result.stdout, /session-private-marker|invalid_api_key/); -}); - -test("accepts only HTTP 200 for health and authenticated Agents reads", async (t) => { - const state = await createLocalState(t); - for (const status of [202, 206]) { - await t.test(`health HTTP ${status}`, async () => { - const { fetchImpl, requests } = await successfulFetchRecorder({ healthStatus: status }); - const result = await runScenario({ - env: { OAC_WEB_DEV_PROXY_TARGET: fixtureTarget }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.match(result.stdout, new RegExp(`Core health endpoint returned HTTP ${status}`)); - assert.deepEqual(requests.map(({ method }) => method), ["GET", "GET"]); - }); - - await t.test(`Agents HTTP ${status}`, async () => { - const { fetchImpl, requests } = await successfulFetchRecorder({ apiStatus: status }); - const result = await runScenario({ - env: { OAC_WEB_DEV_PROXY_TARGET: fixtureTarget }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.match(result.stdout, new RegExp(`basic Agents read failed \\(HTTP ${status}\\)`)); - assert.deepEqual(requests.map(({ method }) => method), ["GET", "GET"]); - }); - } -}); - -test("accepts a canonical non-empty page with additive and unknown tool variants", async (t) => { - const state = await createLocalState(t); - const { fetchImpl, requests } = await successfulFetchRecorder({ - apiBody: JSON.stringify(canonicalAgentPage(canonicalAgent({ - model: "", - multi_agent: { enabled: true, max_concurrent_subagents: 6, additive_nested: true }, - reasoning: { effort: "max", summary: "detailed", additive_nested: true }, - service_tier: "fast", - text: { - format: { type: "json_schema", schema: { type: "object" }, additive_nested: true }, - verbosity: "high", - additive_nested: true, - }, - tools: [ - { type: "function", name: "", description: "", parameters: {}, defer_loading: false }, - { type: "tool_search", additive_nested: true }, - { type: "programmatic_tool_calling", enabled: true }, - canonicalMCPTool({ - transport: { - type: "http", - server_url: "https://mcp.fixture.invalid/tools", - headers: {}, - additive_nested: true, - }, - additive_nested: true, - }), - canonicalMCPTool({ - server_label: "records-without-headers", - transport: { type: "http", server_url: "https://mcp.fixture.invalid/no-headers" }, - }), - { type: "future_tool", additive_nested: true }, - ], - additive_agent: true, - }), { additive_page: true })), - }); - const result = await runScenario({ - env: { OAC_WEB_DEV_PROXY_TARGET: fixtureTarget }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.ok); - assert.match(result.stdout, /Core API authenticated; basic Agent resource envelope parsed/); - assert.match( - result.stdout, - /tool\/Web compatibility, full protocol compatibility, and execution readiness remain unknown/, - ); - assert.equal(requests.length, 2); -}); - -test("rejects malformed or non-canonical Agents list pages", async (t) => { - const state = await createLocalState(t); - const emptyPage = { - object: "list", - data: [], - first_id: null, - last_id: null, - has_more: false, - }; - const missingModel = canonicalAgent(); - delete missingModel.model; - const scenarios = [ - ["wrong list object", { ...emptyPage, object: "agents" }], - ["missing list object", Object.fromEntries(Object.entries(emptyPage).filter(([key]) => key !== "object"))], - ["null Agent", canonicalAgentPage(null, { first_id: null, last_id: null })], - ["missing Agent field", canonicalAgentPage(missingModel)], - ["wrong Agent object", canonicalAgentPage(canonicalAgent({ object: "agent.snapshot" }))], - ["wrong Agent field type", canonicalAgentPage(canonicalAgent({ created_at: "1789438200" }))], - ["wrong nested Agent field type", canonicalAgentPage(canonicalAgent({ - multi_agent: { enabled: "false", max_concurrent_subagents: null }, - }))], - ["null tool", canonicalAgentPage(canonicalAgent({ tools: [null] }))], - ["missing tool discriminant", canonicalAgentPage(canonicalAgent({ tools: [{}] }))], - ["empty tool discriminant", canonicalAgentPage(canonicalAgent({ tools: [{ type: "" }] }))], - ["incomplete function tool", canonicalAgentPage(canonicalAgent({ - tools: [{ type: "function", name: "lookup", description: "", parameters: {} }], - }))], - ["wrong function tool field type", canonicalAgentPage(canonicalAgent({ - tools: [{ type: "function", name: 3, description: "", parameters: {}, defer_loading: false }], - }))], - ["incomplete programmatic tool", canonicalAgentPage(canonicalAgent({ - tools: [{ type: "programmatic_tool_calling" }], - }))], - ["wrong programmatic tool field type", canonicalAgentPage(canonicalAgent({ - tools: [{ type: "programmatic_tool_calling", enabled: "true" }], - }))], - ["incomplete MCP tool", canonicalAgentPage(canonicalAgent({ - tools: [{ type: "mcp" }], - }))], - ["empty MCP server label", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ server_label: "" })], - }))], - ["whitespace-only MCP server label", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ server_label: " " })], - }))], - ["wrong MCP transport type", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ - transport: { type: "stdio", server_url: "https://mcp.fixture.invalid/tools", headers: {} }, - })], - }))], - ["credential-bearing MCP server URL", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ - transport: { type: "http", server_url: "https://user@mcp.fixture.invalid/tools", headers: {} }, - })], - }))], - ["MCP server URL with an empty query", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ - transport: { type: "http", server_url: "https://mcp.fixture.invalid/tools?", headers: {} }, - })], - }))], - ["MCP server URL with surrounding whitespace", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ - transport: { type: "http", server_url: " https://mcp.fixture.invalid/tools", headers: {} }, - })], - }))], - ["wrong MCP header field type", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ - transport: { type: "http", server_url: "https://mcp.fixture.invalid/tools", headers: [] }, - })], - }))], - ["nonempty saved MCP headers", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ - transport: { - type: "http", - server_url: "https://mcp.fixture.invalid/tools", - headers: { authorization: "secret-marker" }, - }, - })], - }))], - ["wrong MCP allow-list item", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ allowed_tools: [""] })], - }))], - ["wrong MCP connection origin", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ connection_origin: "environment" })], - }))], - ["wrong MCP credential field type", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ credential_id: 7 })], - }))], - ["wrong MCP request metadata type", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ request_metadata: [] })], - }))], - ["nonempty saved MCP request metadata", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ request_metadata: { private: "marker" } })], - }))], - ["wrong MCP required field type", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ required: "false" })], - }))], - ["unsupported required MCP server", canonicalAgentPage(canonicalAgent({ - tools: [canonicalMCPTool({ required: true })], - }))], - ["inconsistent disabled multi-agent maximum", canonicalAgentPage(canonicalAgent({ - multi_agent: { enabled: false, max_concurrent_subagents: 4 }, - }))], - ["missing enabled multi-agent maximum", canonicalAgentPage(canonicalAgent({ - multi_agent: { enabled: true, max_concurrent_subagents: null }, - }))], - ["invalid cursor type", { ...emptyPage, first_id: 7 }], - ["empty page with cursor", { ...emptyPage, first_id: "agent_fixture", last_id: "agent_fixture" }], - ["empty page claiming more results", { ...emptyPage, has_more: true }], - ["non-empty page with mismatched cursor", canonicalAgentPage(canonicalAgent(), { last_id: "agent_other" })], - ["missing cursor", Object.fromEntries(Object.entries(emptyPage).filter(([key]) => key !== "last_id"))], - ["more than requested limit", canonicalAgentPage(canonicalAgent(), { - data: [canonicalAgent(), canonicalAgent({ id: "agent_second" })], - last_id: "agent_second", - })], - ]; - - for (const [name, payload] of scenarios) { - await t.test(name, async () => { - const { fetchImpl, requests } = await successfulFetchRecorder({ apiBody: JSON.stringify(payload) }); - const result = await runScenario({ - env: { OAC_WEB_DEV_PROXY_TARGET: fixtureTarget }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.match(result.stdout, /authenticated response did not match the expected list contract/); - assert.equal(requests.length, 2); - }); - } -}); - -test("does not read or authorize from a token file when private permissions cannot be proven", async (t) => { - const state = await createLocalState(t); - const { fetchImpl, requests } = await successfulFetchRecorder(); - const result = await runScenario({ - env: { OAC_WEB_DEV_PROXY_TARGET: fixtureTarget }, - cwd: state.root, - homeDir: state.homeDir, - platform: "win32", - fetchImpl, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.match(result.stdout, /owner-only permissions cannot be verified on this platform; file was not read/); - assert.doesNotMatch(result.stdout, /file is present with private permissions/); - assert.deepEqual(requests.map(({ url }) => url.pathname), ["/healthz"]); - assert.equal(requests[0].headers.has("authorization"), false); - assert.doesNotMatch(result.stdout, new RegExp(fixtureToken)); -}); - -test("loads Vite-style proxy dotenv configuration without exposing unrelated values", async (t) => { - const state = await createLocalState(t); - const { fetchImpl, requests } = await successfulFetchRecorder(); - await writeFile(join(state.root, ".env"), "OAC_WEB_DEV_PROXY_TARGET=https://base.fixture.invalid\n"); - await writeFile( - join(state.root, ".env.local"), - [ - "OAC_WEB_DEV_PROXY_TARGET='https://dotenv.fixture.invalid' # local override", - "OAC_WEB_DEV_PROXY_TOKEN_FILE=${HOME}/.oac/dev/web-token", - "OPENAI_API_KEY=provider-secret-marker", - ].join("\n"), - { mode: 0o600 }, - ); - const result = await runScenario({ - env: { HOME: state.homeDir }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.ok); - assert.equal(requests[0].url.origin, "https://dotenv.fixture.invalid"); - assert.doesNotMatch(result.stdout, /provider-secret-marker/); -}); - -for (const [retiredName, replacement] of [ - ["AGENTS_API_PROXY_TARGET", "OAC_WEB_DEV_PROXY_TARGET"], - ["AGENTS_API_PROXY_TOKEN", "OAC_WEB_DEV_PROXY_TOKEN"], - ["AGENTS_API_PROXY_TOKEN_FILE", "OAC_WEB_DEV_PROXY_TOKEN_FILE"], -]) { - for (const value of ["", "retired-private-value-marker"]) { - test(`rejects retired ${retiredName} (${value ? "set" : "empty"}) before reads or daemon inspection`, async (t) => { - const state = await createLocalState(t); - const result = await runScenario({ - env: { [retiredName]: value, [replacement]: "current-private-value-marker" }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl: async () => assert.fail("retired settings must not make requests"), - runCommand: async () => assert.fail("retired settings must not inspect a daemon"), - }); - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.deepEqual(result.result.checks, [{ - level: "FAIL", layer: "Configuration", - message: `Retired Web settings: ${retiredName} is no longer supported; use ${replacement}.`, - }]); - assert.doesNotMatch(result.stdout + result.stderr, /private-value-marker|fixture-bearer/); - assert.equal(result.stderr, ""); - }); - } -} - -for (const file of [".env", ".env.local", ".env.development", ".env.development.local"]) { - test(`rejects all retired proxy settings in ${file} without expanding or printing their values`, async (t) => { - const state = await createLocalState(t); - await writeFile(join(state.root, file), [ - "AGENTS_API_PROXY_TARGET=https://${OPENAI_API_KEY}.invalid", - "AGENTS_API_PROXY_TOKEN=retired-token-marker", - "AGENTS_API_PROXY_TOKEN_FILE=/private/retired-file-marker", - ].join("\n")); - const result = await runScenario({ - env: { OAC_WEB_DEV_PROXY_TARGET: fixtureTarget, OPENAI_API_KEY: "provider-secret-marker" }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl: async () => assert.fail("retired settings must not make requests"), - runCommand: async () => assert.fail("retired settings must not inspect a daemon"), - }); - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.match(result.stdout, /AGENTS_API_PROXY_TARGET is no longer supported; use OAC_WEB_DEV_PROXY_TARGET/); - assert.match(result.stdout, /AGENTS_API_PROXY_TOKEN is no longer supported; use OAC_WEB_DEV_PROXY_TOKEN/); - assert.match(result.stdout, /AGENTS_API_PROXY_TOKEN_FILE is no longer supported; use OAC_WEB_DEV_PROXY_TOKEN_FILE/); - assert.doesNotMatch(result.stdout + result.stderr, /retired-token-marker|retired-file-marker|provider-secret-marker|fixture-bearer/); - assert.equal(result.result.checks.length, 1); - }); -} - -for (const source of ["environment", ".env"]) { - for (const value of ["", "retired-private-value-marker"]) { - test(`preserves ${source} retirement guidance (${value ? "set" : "empty"}) when a later dotenv path is a directory`, async (t) => { - const state = await createLocalState(t); - await mkdir(join(state.root, ".env.local")); - const env = source === "environment" ? { AGENTS_API_PROXY_TOKEN: value } : {}; - if (source === ".env") await writeFile(join(state.root, ".env"), `AGENTS_API_PROXY_TOKEN=${value}\n`); - const result = await runScenario({ - env, cwd: state.root, homeDir: state.homeDir, - fetchImpl: async () => assert.fail("invalid configuration must not make requests"), - runCommand: async () => assert.fail("invalid configuration must not inspect a daemon"), - }); - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.deepEqual(result.result.checks, [{ - level: "FAIL", layer: "Configuration", - message: "Retired Web settings: AGENTS_API_PROXY_TOKEN is no longer supported; use OAC_WEB_DEV_PROXY_TOKEN.", - }]); - assert.doesNotMatch(result.stdout + result.stderr, /Caller token|retired-private-value-marker|fixture-bearer/); - }); - } -} - -test("stops on an unreadable configuration without inspecting conventional credentials or a daemon", async (t) => { - const state = await createLocalState(t); - await mkdir(join(state.root, ".env.local")); - const result = await runScenario({ - cwd: state.root, homeDir: state.homeDir, - fetchImpl: async () => assert.fail("invalid configuration must not make requests"), - runCommand: async () => assert.fail("invalid configuration must not inspect a daemon"), - }); - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.deepEqual(result.result.checks, [{ - level: "FAIL", layer: "Configuration", message: "local environment configuration is unreadable or unsafe.", - }]); - assert.doesNotMatch(result.stdout + result.stderr, /Caller token|fixture-bearer/); -}); - -test("never falls back to the retired conventional token file", async (t) => { - const state = await createLocalState(t); - await rm(join(state.stateDir, "web-token")); - const retiredDirectory = join(state.homeDir, ".parsar", "agents-api"); - await mkdir(retiredDirectory, { recursive: true }); - await writeFile(join(retiredDirectory, "web-token"), fixtureToken, { mode: 0o600 }); - const { fetchImpl, requests } = await successfulFetchRecorder(); - const result = await runScenario({ - env: { OAC_WEB_DEV_PROXY_TARGET: fixtureTarget }, - cwd: state.root, homeDir: state.homeDir, fetchImpl, - }); - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.match(result.stdout, /Caller token: file is missing or unreadable/); - assert.deepEqual(requests.map(({ url }) => url.pathname), ["/healthz"]); - assert.equal(requests[0].headers.has("authorization"), false); -}); - -test("fails closed when a network target tries to expand an unrelated secret", async (t) => { - const state = await createLocalState(t); - await writeFile( - join(state.root, ".env.local"), - [ - "OPENAI_API_KEY=provider-secret-marker", - "OAC_WEB_DEV_PROXY_TARGET=https://${OPENAI_API_KEY}.invalid", - "OAC_WEB_DEV_PROXY_TOKEN_FILE=${HOME}/.oac/dev/web-token", - ].join("\n"), - { mode: 0o600 }, - ); - let requests = 0; - const result = await runScenario({ - env: { HOME: state.homeDir }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl: async () => { - requests += 1; - return new Response(JSON.stringify({ status: "ok" })); - }, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.equal(requests, 0); - assert.match(result.stdout, /local environment configuration is unreadable or unsafe/); - assert.doesNotMatch(result.stdout, /provider-secret-marker|OPENAI_API_KEY/); -}); - -test("uses an explicit Parsar checkout only for an allowlisted daemon status command", async (t) => { - const state = await createLocalState(t); - const parsarPath = join(state.root, "private-parsar-checkout"); - await mkdir(join(parsarPath, "apps", "daemon", "cmd", "oac-daemon"), { recursive: true }); - await writeFile(join(parsarPath, "go.mod"), "module fixture.invalid/parsar\n"); - await writeFile(join(parsarPath, "apps", "daemon", "cmd", "oac-daemon", "main.go"), "package main\n"); - const { fetchImpl } = await successfulFetchRecorder(); - let commandCall; - const result = await runScenario({ - argv: ["--parsar", parsarPath, "--profile", "fixture-profile"], - env: { - OAC_WEB_DEV_PROXY_TARGET: fixtureTarget, - HOME: state.homeDir, - PATH: "/synthetic/bin", - OPENAI_API_KEY: "provider-secret-marker", - }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl, - runCommand: async (call) => { - commandCall = call; - return { code: 0, stdout: await fixture("daemon-status-absent.txt"), stderr: "" }; - }, - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.ok); - assert.equal(commandCall.command, "go"); - assert.equal(commandCall.cwd, parsarPath); - assert.deepEqual(commandCall.args, [ - "run", - "./apps/daemon/cmd/oac-daemon", - "status", - "--profile", - "fixture-profile", - ]); - assert.equal(commandCall.env.OPENAI_API_KEY, undefined); - assert.doesNotMatch(result.stdout, /private-parsar-checkout|fixture-profile/); -}); - -test("parses only allowlisted daemon state and treats absence as non-fatal", async () => { - assert.equal(parseDaemonStatus(await fixture("daemon-status-paired.txt")), "paired-background-observed"); - assert.equal(parseDaemonStatus(await fixture("daemon-status-absent.txt")), "not-observed"); - assert.equal(parseDaemonStatus("paired: ERROR — /synthetic/private/error"), "unknown"); -}); - -test("never includes credential, response, URL suffix, provider, or private-path markers", async (t) => { - const root = await mkdtemp(join(testRoot, "agents-core-doctor-redaction-")); - t.after(() => rm(root, { recursive: true, force: true })); - const privateDir = join(root, "synthetic-private-doctor-state"); - await mkdir(privateDir, { recursive: true, mode: 0o700 }); - const token = "fixture-super-secret-token"; - const { fetchImpl } = await successfulFetchRecorder({ - apiBody: JSON.stringify({ - object: "list", - data: [canonicalAgent({ name: "session-private-marker" })], - first_id: "agent_fixture", - last_id: "agent_fixture", - has_more: false, - }), - }); - const result = await runScenario({ - env: { - OAC_WEB_DEV_PROXY_TARGET: fixtureTarget, - OAC_WEB_DEV_PROXY_TOKEN: token, - OPENAI_API_KEY: "provider-secret-marker", - HOME: root, - PATH: "/synthetic/bin", - }, - cwd: root, - homeDir: root, - fetchImpl, - runCommand: async () => ({ - code: 0, - stdout: await fixture("daemon-status-paired.txt"), - stderr: "Authorization: Bearer fixture-super-secret-token", - }), - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.ok); - const combined = `${result.stdout}\n${result.stderr}`; - for (const marker of JSON.parse(await fixture("redaction-corpus.json"))) { - assert.equal(combined.includes(marker), false, `report leaked marker: ${marker}`); - } -}); - -test("rejects a malformed target before conventional credential, network or daemon checks", async (t) => { - const state = await createLocalState(t); - const result = await runScenario({ - env: { OAC_WEB_DEV_PROXY_TARGET: "not-a-url" }, - cwd: state.root, homeDir: state.homeDir, - fetchImpl: async () => assert.fail("invalid targets must not make requests"), - runCommand: async () => assert.fail("invalid targets must not inspect a daemon"), - }); - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.deepEqual(result.result.checks, [{ - level: "FAIL", layer: "Configuration", message: "proxy target must be credential-free HTTPS or a loopback HTTP origin.", - }]); - assert.doesNotMatch(result.stdout + result.stderr, /Caller token|fixture-bearer|not-a-url/); -}); - -test("rejects credential-bearing target suffixes without reflecting them", async (t) => { - const state = await createLocalState(t); - const result = await runScenario({ - env: { - OAC_WEB_DEV_PROXY_TARGET: "https://core.fixture.invalid/?access=query-secret-marker#fragment-secret-marker", - HOME: state.homeDir, - }, - cwd: state.root, - homeDir: state.homeDir, - fetchImpl: async () => assert.fail("invalid targets must not be requested"), - runCommand: async () => assert.fail("invalid targets must not inspect a daemon"), - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.diagnosticFailure); - assert.match(result.stdout, /credential-free HTTPS or a loopback HTTP origin/); - assert.equal(result.result.checks.length, 1); - assert.doesNotMatch(result.stdout, /Caller token|query-secret-marker|fragment-secret-marker/); -}); - -test("invalid options use exit 2 without reflecting untrusted argv", async () => { - const result = await runScenario({ - argv: ["--profile", "../../query-secret-marker"], - env: {}, - cwd: process.cwd(), - homeDir: testRoot, - fetchImpl: async () => assert.fail("invalid options must not make a request"), - }); - - assert.equal(result.result.exitCode, CORE_DOCTOR_EXIT_CODES.usageOrInternalError); - assert.equal(result.stdout, ""); - assert.match(result.stderr, /Invalid Core Doctor options/); - assert.doesNotMatch(result.stderr, /query-secret-marker/); -}); diff --git a/scripts/fixtures/core-doctor/agents-list.json b/scripts/fixtures/core-doctor/agents-list.json deleted file mode 100644 index 8435ede10..000000000 --- a/scripts/fixtures/core-doctor/agents-list.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "object": "list", - "data": [], - "first_id": null, - "last_id": null, - "has_more": false -} diff --git a/scripts/fixtures/core-doctor/daemon-status-absent.txt b/scripts/fixtures/core-doctor/daemon-status-absent.txt deleted file mode 100644 index c3b910221..000000000 --- a/scripts/fixtures/core-doctor/daemon-status-absent.txt +++ /dev/null @@ -1,4 +0,0 @@ -profile : fixture -state dir : /synthetic/private/daemon-state -paired : no legacy profile (use the connect command) -background : not started (no connect.pid) diff --git a/scripts/fixtures/core-doctor/daemon-status-paired.txt b/scripts/fixtures/core-doctor/daemon-status-paired.txt deleted file mode 100644 index cabce4533..000000000 --- a/scripts/fixtures/core-doctor/daemon-status-paired.txt +++ /dev/null @@ -1,7 +0,0 @@ -profile : fixture -state dir : /synthetic/private/daemon-state -paired : yes -server_url : https://core.fixture.invalid -runtime_id : synthetic-runtime -hostname : synthetic-host -background : pidfile present at /synthetic/private/connect.pid diff --git a/scripts/fixtures/core-doctor/redaction-corpus.json b/scripts/fixtures/core-doctor/redaction-corpus.json deleted file mode 100644 index 6e4d5a65e..000000000 --- a/scripts/fixtures/core-doctor/redaction-corpus.json +++ /dev/null @@ -1,11 +0,0 @@ -[ - "fixture-super-secret-token", - "Bearer fixture-super-secret-token", - "query-secret-marker", - "fragment-secret-marker", - "provider-secret-marker", - "session-private-marker", - "/synthetic/private/doctor-state", - "/synthetic/private/daemon-state", - "/synthetic/private/connect.pid" -] diff --git a/scripts/name-allowlist.json b/scripts/name-allowlist.json index 02811f914..13861d659 100644 --- a/scripts/name-allowlist.json +++ b/scripts/name-allowlist.json @@ -259,31 +259,6 @@ "regex": "Parsar agent_run id", "reason": "The legacy field documents the originating product wire identifier; its JSON contract is unchanged." }, - { - "path": "scripts/core-doctor.mjs", - "regex": "parsarPath|validateParsarCheckout|--parsar|private-parsar-checkout|fixture\\.invalid/parsar|Parsar (?:protocol baseline|protocol compatibility|Agents API contract)", - "reason": "The optional --parsar argument names an OpenAgentCore checkout whose daemon status command the doctor runs; the installed daemon is oac-daemon. The protocol baseline names the pinned Parsar protocol revision." - }, - { - "path": "scripts/core-doctor.test.mjs", - "regex": "parsarPath|validateParsarCheckout|--parsar|private-parsar-checkout|fixture\\.invalid/parsar|Parsar (?:protocol baseline|protocol compatibility|Agents API contract)", - "reason": "The optional --parsar argument names an OpenAgentCore checkout whose daemon status command the doctor runs; the installed daemon is oac-daemon. The protocol baseline names the pinned Parsar protocol revision." - }, - { - "path": "scripts/core-doctor.mjs", - "regex": "(?:AGENTS_API_|AGENTS_CORE_WEB_)[A-Z0-9_]*\\*?", - "reason": "These Web settings occur only in explicit retirement tables, diagnostics and rejection fixtures." - }, - { - "path": "scripts/core-doctor.test.mjs", - "regex": "(?:AGENTS_API_|AGENTS_CORE_WEB_)[A-Z0-9_]*\\*?", - "reason": "These Web settings occur only in explicit retirement tables, diagnostics and rejection fixtures." - }, - { - "path": "scripts/core-doctor.test.mjs", - "regex": "\"\\.parsar\"|\"agents-api\"", - "reason": "These fixtures prove the old development credential directory is ignored." - }, { "path": "apps/web/src/features/sandbox/deployment-specification.test.ts", "regex": "parsar-core-runtime@sha256:", From 9e6febbe67c336094a7e0c175c584adbda2f3b8c Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 08:29:41 +0000 Subject: [PATCH 3/9] chore: delete the release qualification and promotion scripts No workflow ran them, they needed a private package and host, and they rejected install.sh, so a promoted Release would lack the public bootstrap. release.yml with publish-core-release.py publishes every Release. --- Makefile | 2 - deploy/install/README.md | 2 +- docs/maintainers.md | 4 - scripts/promote-qualified-release.py | 537 ---------------------- scripts/promote-qualified-release.test.py | 466 ------------------- scripts/qualification-control.test.py | 161 ------- scripts/qualification_control.py | 224 --------- scripts/qualify-core-release.py | 190 -------- 8 files changed, 1 insertion(+), 1585 deletions(-) delete mode 100644 scripts/promote-qualified-release.py delete mode 100644 scripts/promote-qualified-release.test.py delete mode 100644 scripts/qualification-control.test.py delete mode 100644 scripts/qualification_control.py delete mode 100644 scripts/qualify-core-release.py diff --git a/Makefile b/Makefile index ea8f24e83..8fea3c0e4 100644 --- a/Makefile +++ b/Makefile @@ -155,8 +155,6 @@ check-distribution: PYTHONDONTWRITEBYTECODE=1 python3 scripts/core-distribution-manifest.test.py PYTHONDONTWRITEBYTECODE=1 python3 scripts/publish-core-release.test.py PYTHONDONTWRITEBYTECODE=1 python3 scripts/install-release.test.py - PYTHONDONTWRITEBYTECODE=1 python3 scripts/promote-qualified-release.test.py - PYTHONDONTWRITEBYTECODE=1 python3 scripts/qualification-control.test.py PYTHONDONTWRITEBYTECODE=1 python3 scripts/config-reference.py --check bash -n deploy/install/install.sh deploy/install-release.sh scripts/build-web.sh scripts/build-core-distribution.sh scripts/prepare-release-runtimes.sh ./scripts/build-web.sh diff --git a/deploy/install/README.md b/deploy/install/README.md index 93fc86806..978cda919 100644 --- a/deploy/install/README.md +++ b/deploy/install/README.md @@ -132,4 +132,4 @@ The Core and node installers share one resolver for these identities. It confirm ## Validation -`make check-distribution` covers the production proxy, the installation rules, release metadata and native catalog assembly, including bundle manifests larger than Node's default subprocess buffer (catalog assembly reads up to 64 MiB). Live release qualification and its stages are in the `scripts/promote-qualified-release.py` docstring. Diagnostics report observed service health, never fabricated model or environment readiness. Runtime observations belong to Core; do not add monitoring or lifecycle tracking to the installer or the landing site. +`make check-distribution` covers the production proxy, the installation rules, release metadata and native catalog assembly, including bundle manifests larger than Node's default subprocess buffer (catalog assembly reads up to 64 MiB). Diagnostics report observed service health, never fabricated model or environment readiness. Runtime observations belong to Core; do not add monitoring or lifecycle tracking to the installer or the landing site. diff --git a/docs/maintainers.md b/docs/maintainers.md index b1bec0177..21720481d 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -144,10 +144,6 @@ gh workflow run core-release --repo MiniMax-AI/OpenAgentCore --ref main \ With `draft_release=true` the result is an unpublished `build-` draft Release; with `draft_release=false` the files stay in the Actions artifact. Use the exact matched asset set; never mix builds or resolve components through `latest`. -### Promote a qualified candidate - -`scripts/promote-qualified-release.py` qualifies a candidate on a supervised host and publishes it once main reaches the reviewed promotion commit. Pass the candidate's flat files, built for the `build-` release base: the thin and offline archives and the native installers, each with its `.sha256`, and the Runtime and node assets. Take them from a local build with that release base and `CORE_DISTRIBUTION_OFFLINE=1`, or from the Actions artifact of a manual `core-release` run with `draft_release=false` after removing `install.sh` and `install.sh.sha256`. The command creates the draft Release itself and refuses any other file, so a draft created by `draft_release=true` cannot be promoted. Its module docstring lists the inputs, the qualification stages and the publication checks. - ## Continuous integration | Workflow | Runs on | Covers | diff --git a/scripts/promote-qualified-release.py b/scripts/promote-qualified-release.py deleted file mode 100644 index bae72ec3c..000000000 --- a/scripts/promote-qualified-release.py +++ /dev/null @@ -1,537 +0,0 @@ -#!/usr/bin/env python3 -"""Qualify one candidate on a supervised host, then publish it once its promotion commit lands. - -One invocation owns one explicit candidate and one qualification run, using the -existing gh authentication and SSH. It never builds: pass the candidate's flat -files, and it creates the unpublished build- draft itself. Produce the files -with make build-core-distribution for that commit, with -CORE_DISTRIBUTION_RELEASE_BASE_URL set to -https://github.com/MiniMax-AI/OpenAgentCore/releases/download/build-, -CORE_DISTRIBUTION_OFFLINE=1 and the native installer catalog, or take the Actions -artifact of a manual core-release run with draft_release=false and remove -install.sh and install.sh.sha256. A draft that core-release created holds -install.sh and cannot be promoted. - -Inputs -- --source: the full candidate commit, never inferred from latest. It binds the - archive manifests and the build- release tag. -- --assets: only the candidate's flat files: the thin and offline archives with - their checksums, the Runtime assets and the native installers with their - checksums. Any other file, including install.sh, is refused. Every archive - member and asset hash is verified, and the thin and offline manifests and native - catalogs must match. -- --qualification-package, --qualification-manifest-sha256: a separately reviewed - private package and its manifest digest. The manifest fixes the complete file - inventory, ordered Python commands, bounded stage timeouts and private path and - resource configuration. It is verified before any Release change and again by - the remote supervisor; candidate assets cannot select or replace it. Keep - host-specific scripts, user names and credential paths out of this repository, - and never put credential values in either manifest. -- --promotion-commit: the independently reviewed tooling commit; its tree is the - one main must reach. It must descend from the candidate, and the local promotion - scripts must equal their bytes there. It may differ from the candidate only in - PROMOTION_FILES, and the Makefile only by registering the promotion and - control-channel tests. Product changes or a different main tree block promotion. -- --host, --remote-root: an existing SSH host alias and an isolated remote parent. -- --state: a new private local evidence directory; a previous one is never reused. -- --merge-wait-seconds: how long to wait for main to reach the promotion commit's - tree; one day by default, at most seven days. - -Flow -1. Check the qualification adapter, the tooling bytes, the candidate files and - the source tree. Refuse a conflicting tag, a published Release or a draft for - another commit, and create the build- draft with these files when none - exists. Download every asset and compare its bytes. -2. Copy the assets and the package to a fresh /. The reviewed - adapter supervises fresh-install, current-lifecycle, managed-native-smoke, - diagnostics-observations-smoke and node-runtime-smoke in that order: one fresh - container installation, one completed managed Session, read-only diagnostics - for it, and one current Runtime Session on one new node. The full multi-host, - generation and GC matrix is not rerun. Every check must pass in this run and - return this run's identity and its own owned resources; a supplied pass file, - skipped check or old report never releases the candidate. Candidate bytes are - verified again after the stages. -3. In the same process, wait until main's tree equals the promotion commit's tree - and main contains the candidate. A main that is behind waits; a conflicting - main fails at once. Expiry or cancellation keeps the evidence and grants no - later permission to publish. -4. Check the tag and that the same draft ID is still an unpublished draft, and - download every asset again. Then recheck main, the tree, the tag and the draft - ID, publish that Release ID (never a fresh tag lookup) as the latest release, - and download once more to check the published bytes. - -The SSH stdin channel carries the request and then heartbeats; EOF, timeout, -SIGTERM or SIGHUP stops later work. Each stage runs in its own foreground process -group with an owner outside it that cleans the group on success, failure, timeout -and cancellation, including descendants orphaned by an inner timeout or SIGKILL. -Only explicitly recorded background resources may detach. A write already issued -may have an unknown outcome: keep its intent and resources, never replay it or -claim a rollback. Control tests use short-lived fixture children and never count -as live qualification. - -Never overwrite conflicting assets. Reconcile an interrupted run before invoking -again; stored results are evidence, not permission to publish. The command never -merges pull requests or decides what lands on main; it only waits for main. No -runner, background service, GitHub secret or repository visibility change is -needed. -""" - -import argparse -import base64 -import hashlib -import importlib.util -import json -import pathlib -import re -import shlex -import shutil -import subprocess -import tarfile -import time -import tempfile -import uuid - -spec = importlib.util.spec_from_file_location( - "distribution", pathlib.Path(__file__).with_name("core-distribution-manifest.py")) -distribution = importlib.util.module_from_spec(spec) -spec.loader.exec_module(distribution) - -REPO = "MiniMax-AI/OpenAgentCore" -SOURCE = TAG = BASE = STEM = None - - -def select_source(source): - """One invocation owns one explicit immutable candidate identity.""" - if not isinstance(source, str) or not re.fullmatch(r"[0-9a-f]{40}", source): - raise ValueError("An explicit full candidate source commit is required") - global SOURCE, TAG, BASE, STEM - SOURCE = source - TAG = "build-" + source - BASE = "https://github.com/" + REPO + "/releases/download/" + TAG - STEM = "oac-" + source + "-linux-amd64" - - -adapter_spec = importlib.util.spec_from_file_location( - "qualification_adapter", pathlib.Path(__file__).with_name("qualify-core-release.py")) -qualification_adapter = importlib.util.module_from_spec(adapter_spec) -adapter_spec.loader.exec_module(qualification_adapter) - -REQUIRED_CHECKS = qualification_adapter.CHECKS -# Only this separate promotion change may follow the qualified source on main. -PROMOTION_FILES = { - ".github/workflows/release.yml", "AGENTS.md", "CONTRIBUTING.md", "docs/maintainers.md", - "Makefile", "contracts/agents-api/node-generation-protocol.md", - "scripts/promote-qualified-release.py", - "scripts/promote-qualified-release.test.py", "scripts/qualify-core-release.py", - "scripts/qualification_control.py", "scripts/qualification-control.test.py", -} - - -def run(argv, **kwargs): - return subprocess.run(argv, check=True, text=True, capture_output=True, **kwargs).stdout - - -def gh(*args): - return run(["gh", *args, "--repo", REPO]) - - -def api(path): - return json.loads(run(["gh", "api", "repos/" + REPO + "/" + path])) - - -def digest(stream): - result = hashlib.sha256() - for chunk in iter(lambda: stream.read(1024 * 1024), b""): - result.update(chunk) - return result.hexdigest() - - -def file_identity(path): - if path.is_symlink() or not path.is_file(): - raise ValueError("Asset must be a regular file: " + path.name) - with path.open("rb") as stream: - return {"sha256": digest(stream), "size": path.stat().st_size} - - -def canonical(value): - return json.dumps(value, sort_keys=True, separators=(",", ":")) - - -def verify_archive(path, offline, native=None): - """Read without extraction; verify every ordinary member and optional payload.""" - hashes, metadata, sums = {}, None, None - catalog = {} - with tarfile.open(path, "r|gz") as archive: - for member in archive: - parts = pathlib.PurePosixPath(member.name).parts - if (not member.isfile() or len(parts) < 2 or parts[0] != STEM - or ".." in parts or member.name in hashes): - raise ValueError("Invalid archive member") - relative = "/".join(parts[1:]) - stream = archive.extractfile(member) - if relative in ("manifest.json", "SHA256SUMS", "native-installers/catalog.json"): - if member.size > 1024 * 1024: - raise ValueError("Oversized archive metadata") - raw = stream.read() - hashes[member.name] = hashlib.sha256(raw).hexdigest() - if relative == "manifest.json": - metadata = json.loads(raw) - elif relative == "SHA256SUMS": - sums = raw.decode() - else: - catalog = json.loads(raw) - if native is not None: - native.update(catalog) - else: - hashes[member.name] = digest(stream) - if metadata is None or sums is None: - raise ValueError("Missing archive manifest/checksums") - if (metadata.get("source_commit") != SOURCE - or metadata.get("artifact_base_url") != BASE - or metadata.get("platform") != "linux/amd64" - or not re.fullmatch(r"[0-9a-f]{40}", metadata.get("source_tree", ""))): - raise ValueError("Candidate source/platform/release URL mismatch") - expected = {} - for line in sums.splitlines(): - checksum, name = line.split(" ", 1) - if name in expected or not re.fullmatch(r"[0-9a-f]{64}", checksum): - raise ValueError("Invalid archive checksum list") - expected[name] = checksum - artifacts = metadata.get("artifacts", {}) - if set(artifacts) != set(distribution.ARTIFACTS): - raise ValueError("Missing or unexpected Runtime artifacts") - for key in ("images", "image_manifest_digests"): - values = metadata.get(key, {}) - if set(values) != {"core", "web", "runtime", "database", "ingress"} or any( - not distribution.DIGEST.fullmatch(value) for value in values.values()): - raise ValueError("Missing immutable image identities") - if not re.fullmatch(r"oac-runtime@sha256:[0-9a-f]{64}", metadata.get("runtime_ref", "")): - raise ValueError("Missing immutable Runtime identity") - for logical, entry in artifacts.items(): - name = entry["filename"] - if name != STEM + "-" + distribution.ARTIFACTS[logical]: - raise ValueError("Unexpected Runtime filename") - if not re.fullmatch(re.escape(STEM) + r"-[A-Za-z0-9_.-]+", name): - raise ValueError("Invalid Runtime asset name") - if offline: - expected["artifacts/" + name] = entry["sha256"] - if offline: - for platform, entry in catalog.get("artifacts", {}).items(): - expected["native-installers/" + platform + ".tar.gz"] = entry["sha256"] - actual = {name[len(STEM) + 1:]: value for name, value in hashes.items() - if name != STEM + "/SHA256SUMS"} - if actual != expected: - raise ValueError("Archive member checksum/set mismatch") - return metadata - - -def inspect_candidate(directory): - files = {p.name: file_identity(p) for p in directory.iterdir()} - native, online_native = {}, {} - metadata = verify_archive(directory / (STEM + "-offline.tar.gz"), True, native) - if verify_archive(directory / (STEM + ".tar.gz"), False, online_native) != metadata or native != online_native: - raise ValueError("Thin and offline manifests differ") - expected = set() - for suffix in (".tar.gz", "-offline.tar.gz"): - name = STEM + suffix - expected.update((name, name + ".sha256")) - if (directory / (name + ".sha256")).read_text() != files[name]["sha256"] + " " + name + "\n": - raise ValueError("Archive checksum mismatch") - for entry in metadata["artifacts"].values(): - name = entry["filename"] - expected.add(name) - if files.get(name) != {key: entry[key] for key in ("sha256", "size")}: - raise ValueError("Runtime asset checksum/size mismatch") - if native: - if native["version"] != SOURCE: - raise ValueError("Native catalog source mismatch") - for platform, entry in native["artifacts"].items(): - if not re.fullmatch(r"(linux|darwin|windows)-(amd64|arm64)", platform): - raise ValueError("Invalid native platform") - name = f"oac-native-{SOURCE}-{platform}.tar.gz" - expected.update((name, name + ".sha256")) - if (entry.get("url") != BASE + "/" + name or files.get(name, {}).get("sha256") != entry["sha256"] - or (directory / (name + ".sha256")).read_text() != entry["sha256"] + " " + name + "\n"): - raise ValueError("Native asset URL/checksum mismatch") - if set(files) != expected: - raise ValueError("Candidate asset set mismatch; provide only generated flat files") - return metadata, files - - -def verify_files(directory, inventory): - actual = {p.name: file_identity(p) for p in directory.iterdir()} - if actual != inventory: - raise ValueError("Release asset bytes/set changed") - - -def verify_landed(tree, promotion_commit, allow_pending=False): - source = api("commits/" + SOURCE) - if source["commit"]["tree"]["sha"] != tree: - raise ValueError("Source tree differs from GitHub commit") - comparison = api("compare/" + SOURCE + "..." + promotion_commit) - if comparison["status"] not in ("identical", "ahead"): - raise ValueError("Promotion commit does not descend from qualified source") - if any(f["filename"] not in PROMOTION_FILES or f.get("previous_filename", f["filename"]) not in PROMOTION_FILES - for f in comparison.get("files", [])): - raise ValueError("Promotion commit contains product changes") - if any(f["filename"] == "Makefile" for f in comparison.get("files", [])): - original = base64.b64decode(api("contents/Makefile?ref=" + SOURCE)["content"]).decode() - updated = base64.b64decode(api("contents/Makefile?ref=" + promotion_commit)["content"]).decode() - anchor = "\tPYTHONDONTWRITEBYTECODE=1 python3 scripts/core-distribution-manifest.test.py\n" - addition = ("\tPYTHONDONTWRITEBYTECODE=1 python3 scripts/promote-qualified-release.test.py\n" - "\tPYTHONDONTWRITEBYTECODE=1 python3 scripts/qualification-control.test.py\n") - if original.count(anchor) != 1 or updated != original.replace(anchor, anchor + addition): - raise ValueError("Makefile change exceeds promotion test registration") - reviewed_tree = api("commits/" + promotion_commit)["commit"]["tree"]["sha"] - main = api("commits/main") - if main["commit"]["tree"]["sha"] != reviewed_tree: - if allow_pending: - pending = api("compare/" + main["sha"] + "..." + promotion_commit) - if pending["status"] == "ahead": - return False - raise ValueError("Main tree differs from reviewed promotion commit") - comparison = api("compare/" + SOURCE + "..." + main["sha"]) - if comparison["status"] not in ("identical", "ahead"): - raise ValueError("Qualified source has not landed on main") - return True - - -def wait_for_landed(tree, promotion_commit, timeout_seconds): - deadline = time.monotonic() + timeout_seconds - while not verify_landed(tree, promotion_commit, allow_pending=True): - remaining = deadline - time.monotonic() - if remaining <= 0: - raise TimeoutError("Reviewed batch did not land within the merge wait budget") - # Qualification remains in this process; no saved pass file is reloaded. - time.sleep(min(30, remaining)) - - -def release_state(): - # Listing avoids treating authentication/network errors as a missing release. - pages = json.loads(run(["gh", "api", "--paginate", "--slurp", - "repos/" + REPO + "/releases?per_page=100"])) - matches = [release for page in pages for release in page if release["tag_name"] == TAG] - if len(matches) > 1: - raise ValueError("Ambiguous release identity") - return matches[0] if matches else None - - -def verify_tag(required=False): - # gh api's matching-refs endpoint returns [] when the exact tag is absent. - refs = api("git/matching-refs/tags/" + TAG) - refs = [ref for ref in refs if ref["ref"] == "refs/tags/" + TAG] - if required and not refs: - raise ValueError("Published release tag is missing") - if refs and (refs[0]["object"]["type"] != "commit" or refs[0]["object"]["sha"] != SOURCE): - raise ValueError("Conflicting release tag") - - -def download(evidence, inventory): - release = release_state() - if not release: - raise ValueError("Release disappeared") - pages = json.loads(run(["gh", "api", "--paginate", "--slurp", - "repos/" + REPO + "/releases/" + str(release["id"]) + "/assets?per_page=100"])) - assets = [asset for page in pages for asset in page] - if len(assets) != len(inventory) or {asset["name"] for asset in assets} != set(inventory): - raise ValueError("Release asset set mismatch") - for name, expected in sorted(inventory.items()): - command = ["gh", "release", "download", TAG, "--repo", REPO, - "--pattern", name, "--output", "-", "--allow-escape-sequences"] - # Stream binary downloads into the hash, never into another archive copy. - with tempfile.TemporaryFile() as errors: - with subprocess.Popen(command, stdout=subprocess.PIPE, stderr=errors) as process: - checksum, size = hashlib.sha256(), 0 - for block in iter(lambda: process.stdout.read(1024 * 1024), b""): - checksum.update(block) - size += len(block) - status = process.wait() - if status: - raise subprocess.CalledProcessError(status, command) - if {"sha256": checksum.hexdigest(), "size": size} != expected: - raise ValueError("Downloaded release asset bytes changed: " + name) - evidence.with_suffix(".json").write_text(canonical(inventory) + "\n") - - -def verify_remote(request, host): - code = """import hashlib,json,pathlib,sys -request=json.load(sys.stdin) -directory=pathlib.Path(request['directory']) -expected=request['inventory'] -actual={} -for name in expected: - path=directory/name - if path.is_symlink() or not path.is_file(): raise ValueError('Invalid remote asset') - digest=hashlib.sha256() - with path.open('rb') as stream: - for block in iter(lambda:stream.read(1024*1024),b''): digest.update(block) - actual[name]={'sha256':digest.hexdigest(),'size':path.stat().st_size} -if actual != expected: raise ValueError('Remote candidate bytes changed') -""" - run(["ssh", "-o", "BatchMode=yes", host, shlex.join(["python3", "-c", code])], - input=canonical(request)) - - -def qualification(request, host, remote, adapter, downloaded, package, manifest_hash): - adapter_bytes = adapter.read_bytes() - request["adapter_sha256"] = hashlib.sha256(adapter_bytes).hexdigest() - control_hash = file_identity(adapter.with_name("qualification_control.py"))["sha256"] - command = shlex.join(["mkdir", "-m", "700", remote]) - run(["ssh", "-o", "BatchMode=yes", host, command]) - run(["scp", "-q", "--", str(adapter), str(adapter.with_name("qualification_control.py")), - *[str(p) for p in sorted(downloaded.iterdir())], - host + ":" + remote + "/"]) - qualification_adapter.verify_package(package, manifest_hash) - run(["scp", "-q", "-r", "--", str(package), host + ":" + remote + "/tools"]) - request["qualification_package"] = remote + "/tools" - request["qualification_manifest_sha256"] = manifest_hash - verify_remote(request, host) - # Hash and execute the same bytes, avoiding a check-then-open script race. - bootstrap = ("import hashlib,pathlib,sys,types; p=pathlib.Path(sys.argv[1]); b=p.read_bytes(); " - "hashlib.sha256(b).hexdigest()==sys.argv[2] or sys.exit('Adapter bytes changed'); " - "c=p.with_name('qualification_control.py'); raw=c.read_bytes(); " - "hashlib.sha256(raw).hexdigest()==sys.argv[3] or sys.exit('Control bytes changed'); " - "m=types.ModuleType('qualification_control'); m.__file__=str(c); " - "exec(compile(raw,str(c),'exec'),m.__dict__); sys.modules[m.__name__]=m; " - "sys.argv=[str(p)]; exec(compile(b,str(p),'exec'),{'__name__':'__main__','__file__':str(p)})") - argv = ["python3", "-c", bootstrap, remote + "/" + adapter.name, request["adapter_sha256"], control_hash] - response = qualification_adapter.control.transport( - ["ssh", "-o", "BatchMode=yes", host, shlex.join(argv)], request) - verify_remote(request, host) - result = json.loads(response) - required = {"source": SOURCE, "tree": request["tree"], "run_id": request["run_id"], - "inventory_sha256": request["inventory_sha256"], "adapter_sha256": request["adapter_sha256"]} - if any(result.get(key) != value for key, value in required.items()): - raise ValueError("Qualification identity mismatch") - if (result.get("status") != "passed" or result.get("qualification_manifest_sha256") != manifest_hash - or result.get("checks") != {name: "passed" for name in REQUIRED_CHECKS}): - raise ValueError("Qualification checks missing, failed or skipped") - return result - - -def verify_tooling(promotion_commit): - for name in ("promote-qualified-release.py", "qualify-core-release.py", "qualification_control.py", "core-distribution-manifest.py"): - expected = base64.b64decode(api("contents/scripts/" + name + "?ref=" + promotion_commit)["content"]) - if pathlib.Path(__file__).with_name(name).read_bytes() != expected: - raise ValueError("Local tooling differs from reviewed commit: " + name) - - -def publish_release(release_id, body_file): - if type(release_id) is not int or release_id <= 0: - raise ValueError("Invalid verified Release ID") - updated = json.loads(run(["gh", "api", "--method", "PATCH", "repos/" + REPO + "/releases/" + str(release_id), - "--input", str(body_file)])) - if updated.get("id") != release_id or updated.get("draft") is not False or updated.get("prerelease") is not False: - raise ValueError("Publication response identity mismatch") - - -def promote(assets, state, host, remote_root, promotion_commit, *, source, package, manifest_hash, - merge_wait_seconds=86400): - select_source(source) - if type(merge_wait_seconds) is not int or not 1 <= merge_wait_seconds <= 604800: - raise ValueError("Merge wait must be between one second and seven days") - qualification_adapter.verify_package(package, manifest_hash) - if package == assets or package.is_relative_to(assets): - raise ValueError("Qualification tooling must be separate from candidate assets") - if not re.fullmatch(r"[0-9a-f]{40}", promotion_commit): - raise ValueError("A reviewed full promotion commit is required") - if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.@-]*", host): - raise ValueError("Invalid SSH host") - if (not re.fullmatch(r"/[A-Za-z0-9_./-]+", remote_root) - or ".." in pathlib.PurePosixPath(remote_root).parts): - raise ValueError("Remote root must be an absolute safe path") - adapter = pathlib.Path(__file__).with_name("qualify-core-release.py") - description = json.loads(run(["python3", str(adapter), "--describe"])) - if description != {"ready": True, "required_checks": list(REQUIRED_CHECKS)}: - raise ValueError("Live qualification adapter is not connected; no release changes made") - verify_tooling(promotion_commit) - state.mkdir(mode=0o700) # Never resume a previous pass record. - metadata, inventory = inspect_candidate(assets) - (state / "inventory.json").write_text(canonical(inventory) + "\n") - staged_bytes = sum(entry["size"] for entry in inventory.values()) - reserve = 64 * 1024 * 1024 - if shutil.disk_usage(state).free < reserve: - raise ValueError("Insufficient local evidence/streaming disk reserve") - (state / "capacity.json").write_text(canonical({"existing_asset_bytes": staged_bytes, - "additional_reserve_bytes": reserve}) + "\n") - if api("commits/" + SOURCE)["commit"]["tree"]["sha"] != metadata["source_tree"]: - raise ValueError("Candidate source tree mismatch") - verify_tag() - existing = release_state() - if existing and existing["target_commitish"] != SOURCE: - raise ValueError("Draft source target mismatch") - if existing and not existing["draft"]: - raise ValueError("Release already published; reconcile without rerunning acceptance") - if not existing: - notes = state / "release-notes.md" - notes.write_text("Fresh-install offline distribution from " + SOURCE + ".\n") - gh("release", "create", TAG, "--draft", "--target", SOURCE, - "--title", "OpenAgentCore " + SOURCE, "--notes-file", str(notes), - *[str(assets / name) for name in sorted(inventory)]) - downloaded = state / "downloaded" - download(downloaded, inventory) - initial_release = release_state() - if not initial_release or not initial_release["draft"] or initial_release["target_commitish"] != SOURCE: - raise ValueError("Draft identity changed") - request = {"source": SOURCE, "tree": metadata["source_tree"], "run_id": str(uuid.uuid4()), - "inventory": inventory, "inventory_sha256": hashlib.sha256(canonical(inventory).encode()).hexdigest(), - "required_checks": list(REQUIRED_CHECKS)} - remote = remote_root.rstrip("/") + "/" + request["run_id"] - request["directory"] = remote - result = qualification(request, host, remote, - adapter, assets, package, manifest_hash) - (state / "request.json").write_text(canonical(request) + "\n") - (state / "qualification.json").write_text(canonical(result) + "\n") - verify_files(assets, inventory) - wait_for_landed(metadata["source_tree"], promotion_commit, merge_wait_seconds) - verify_tag() - current = release_state() - if (not current or not current["draft"] or current["id"] != initial_release["id"] - or current["target_commitish"] != SOURCE): - raise ValueError("Draft replaced or published during qualification") - download(state / "before-publication", inventory) - notes = state / "release-notes.md" - notes.write_text("Fresh-install offline distribution from " + SOURCE + ".\n\n" - + "Real smoke checks: " + ", ".join(REQUIRED_CHECKS) + ".\n" - + "The full multi-host, generation and GC matrix was not rerun for this promotion.\n" - + "Asset inventory SHA256: " + request["inventory_sha256"] + ".\n" - + "Promotion tooling commit: " + promotion_commit + ".\n") - # Downloading can take minutes. Revalidate immediately before mutation. - verify_landed(metadata["source_tree"], promotion_commit) - verify_tag() - final_draft = release_state() - if (not final_draft or final_draft["id"] != initial_release["id"] or not final_draft["draft"] - or final_draft["target_commitish"] != SOURCE): - raise ValueError("Draft identity changed during final verification") - publication = state / "publication.json" - publication.write_text(canonical({"draft": False, "prerelease": False, "make_latest": "true", - "body": notes.read_text()}) + "\n") - publish_release(final_draft["id"], publication) - download(state / "published", inventory) - final = release_state() - if not final or final["id"] != current["id"] or final["draft"] or final["prerelease"]: - raise ValueError("Final publication state mismatch") - verify_tag(required=True) - print(final["html_url"]) - - -def main(): - parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) - parser.add_argument("--source", required=True, help="Full candidate source commit; never inferred from latest") - parser.add_argument("--qualification-package", required=True, type=pathlib.Path, - help="Separately reviewed private package directory, containing manifest.json") - parser.add_argument("--qualification-manifest-sha256", required=True, help="Explicit reviewed manifest digest") - parser.add_argument("--merge-wait-seconds", type=int, default=86400, help="Same-process merge wait, at most seven days") - parser.add_argument("--promotion-commit", required=True, help="Reviewed tooling commit, never artifact provenance") - parser.add_argument("--assets", required=True, type=pathlib.Path, help="Flat candidate files only") - parser.add_argument("--state", required=True, type=pathlib.Path, help="New local evidence directory") - parser.add_argument("--host", required=True, help="Existing SSH host alias") - parser.add_argument("--remote-root", required=True, help="Existing isolated remote parent directory") - args = parser.parse_args() - promote(args.assets.resolve(), args.state.resolve(), args.host, args.remote_root, args.promotion_commit, - source=args.source, package=args.qualification_package.resolve(), - manifest_hash=args.qualification_manifest_sha256, merge_wait_seconds=args.merge_wait_seconds) - - -if __name__ == "__main__": - main() diff --git a/scripts/promote-qualified-release.test.py b/scripts/promote-qualified-release.test.py deleted file mode 100644 index cc85a345d..000000000 --- a/scripts/promote-qualified-release.test.py +++ /dev/null @@ -1,466 +0,0 @@ -"""Controller refusal tests; these fixtures never count as live qualification.""" - -import hashlib -import importlib.util -import io -import json -import pathlib -import subprocess -import sys -import tarfile -import tempfile -import unittest -from unittest import mock - -spec = importlib.util.spec_from_file_location( - "promotion", pathlib.Path(__file__).with_name("promote-qualified-release.py")) -PathControl = pathlib.Path(__file__).with_name('qualification_control.py') -promotion = importlib.util.module_from_spec(spec) -spec.loader.exec_module(promotion) - - -class PromotionTests(unittest.TestCase): - def setUp(self): - promotion.select_source("a" * 40) - self.temp = tempfile.TemporaryDirectory() - self.addCleanup(self.temp.cleanup) - self.root = pathlib.Path(self.temp.name) - self.assets = self.root / "assets" - self.assets.mkdir() - self.package = self.root / "private-package" - self.package.mkdir() - (self.package / "fixture.py").write_text("# Control fixture, never live qualification\n") - self.manifest = {"version": 1, "files": {"fixture.py": promotion.file_identity(self.package / "fixture.py")}, - "configuration": {}, "stages": [{"name": name, "python": sys.executable, - "script": "fixture.py", "args": [], "timeout_seconds": 3} for name in promotion.REQUIRED_CHECKS]} - (self.package / "manifest.json").write_text(promotion.canonical(self.manifest)) - self.manifest_hash = promotion.file_identity(self.package / "manifest.json")["sha256"] - self.kwargs = dict(source=promotion.SOURCE, package=self.package, manifest_hash=self.manifest_hash) - self.tree = "b" * 40 - self.metadata = { - "source_commit": promotion.SOURCE, "source_tree": self.tree, - "artifact_base_url": promotion.BASE, "platform": "linux/amd64", - "images": {name: "sha256:" + "a" * 64 for name in ("core", "web", "runtime", "database", "ingress")}, - "image_manifest_digests": {name: "sha256:" + "b" * 64 for name in ("core", "web", "runtime", "database", "ingress")}, - "runtime_ref": "oac-runtime@sha256:" + "c" * 64, "artifacts": {}, - } - for logical, suffix in promotion.distribution.ARTIFACTS.items(): - name = promotion.STEM + "-" + suffix - (self.assets / name).write_bytes(logical.encode()) - self.metadata["artifacts"][logical] = dict(promotion.file_identity(self.assets / name), filename=name) - self.archives() - - def archives(self): - for offline in (False, True): - data = {"manifest.json": json.dumps(self.metadata).encode(), "source.tar.gz": b"source fixture"} - if hasattr(self, "native"): - data["native-installers/catalog.json"] = json.dumps(self.native).encode() - if offline: - for platform in self.native["artifacts"]: - data["native-installers/" + platform + ".tar.gz"] = b"native" - data["SHA256SUMS"] = "".join( - hashlib.sha256(value).hexdigest() + " " + key + "\n" for key, value in data.items() - if not (key.startswith("native-installers/") and key.endswith(".tar.gz"))).encode() - if offline: - data.update({"artifacts/" + entry["filename"]: (self.assets / entry["filename"]).read_bytes() - for entry in self.metadata["artifacts"].values()}) - name = promotion.STEM + ("-offline" if offline else "") + ".tar.gz" - with tarfile.open(self.assets / name, "w:gz") as archive: - for key, value in data.items(): - entry = tarfile.TarInfo(promotion.STEM + "/" + key) - entry.size = len(value) - archive.addfile(entry, io.BytesIO(value)) - checksum = promotion.file_identity(self.assets / name)["sha256"] - (self.assets / (name + ".sha256")).write_text(checksum + " " + name + "\n") - - def test_independent_native_assets_remain_in_qualification_inventory(self): - name = f"oac-native-{promotion.SOURCE}-linux-amd64.tar.gz" - digest = hashlib.sha256(b"native").hexdigest() - (self.assets / name).write_bytes(b"native") - (self.assets / (name + ".sha256")).write_text(digest + " " + name + "\n") - self.native = {"version": promotion.SOURCE, "artifacts": {"linux-amd64": { - "sha256": digest, "url": promotion.BASE + "/" + name}}} - self.archives() - _, inventory = promotion.inspect_candidate(self.assets) - self.assertIn(name, inventory) - (self.assets / name).unlink() - with self.assertRaisesRegex(ValueError, "Native asset"): - promotion.inspect_candidate(self.assets) - - def test_full_asset_inventory_and_archive_contents(self): - metadata, inventory = promotion.inspect_candidate(self.assets) - self.assertEqual(metadata, self.metadata) - promotion.verify_files(self.assets, inventory) - name = next(iter(self.metadata["artifacts"].values()))["filename"] - (self.assets / name).write_bytes(b"changed") - with self.assertRaisesRegex(ValueError, "checksum/size"): - promotion.inspect_candidate(self.assets) - with self.assertRaisesRegex(ValueError, "bytes/set"): - promotion.verify_files(self.assets, inventory) - - def test_source_and_url_mismatch(self): - for key in ("source_commit", "artifact_base_url"): - with self.subTest(key=key): - old = self.metadata[key] - self.metadata[key] = "wrong" - self.archives() - with self.assertRaisesRegex(ValueError, "source/platform/release URL"): - promotion.inspect_candidate(self.assets) - self.metadata[key] = old - - def test_missing_or_extra_asset_rejected(self): - extra = self.assets / "pass.json" - extra.write_text('{"passed":true}') - with self.assertRaisesRegex(ValueError, "asset set mismatch"): - promotion.inspect_candidate(self.assets) - extra.unlink() - name = next(iter(self.metadata["artifacts"].values()))["filename"] - (self.assets / name).unlink() - with self.assertRaisesRegex(ValueError, "checksum/size"): - promotion.inspect_candidate(self.assets) - - def test_missing_manifest_artifact_rejected(self): - self.metadata["artifacts"].pop(next(iter(self.metadata["artifacts"]))) - self.archives() - with self.assertRaisesRegex(ValueError, "Missing or unexpected"): - promotion.inspect_candidate(self.assets) - - def test_unlanded_and_unrelated_main_changes_rejected(self): - for comparison in ({"status": "diverged", "files": []}, - {"status": "ahead", "files": [{"filename": "services/core/main.go"}]}, - {"status": "ahead", "files": [{"filename": "Makefile", "previous_filename": "go.mod"}]}): - with self.subTest(comparison=comparison), mock.patch.object( - promotion, "api", side_effect=[{"commit": {"tree": {"sha": self.tree}}}, comparison]): - with self.assertRaises(ValueError): - promotion.verify_landed(self.tree, "d" * 40) - - def test_landed_source_keeps_original_identity(self): - with mock.patch.object(promotion, "api", side_effect=[ - {"commit": {"tree": {"sha": self.tree}}}, - {"status": "ahead", "files": [{"filename": "scripts/promote-qualified-release.py"}]}, - {"commit": {"tree": {"sha": "e" * 40}}}, - {"sha": "f" * 40, "commit": {"tree": {"sha": "e" * 40}}}, - {"status": "ahead"}, - ]): - promotion.verify_landed(self.tree, "d" * 40) - - def test_conflicting_tag_rejected(self): - with mock.patch.object(promotion, "api", return_value=[{ - "ref": "refs/tags/" + promotion.TAG, "object": {"type": "commit", "sha": "f" * 40}, - }]): - with self.assertRaisesRegex(ValueError, "Conflicting"): - promotion.verify_tag() - - def test_main_tree_mismatch_blocks_old_candidate(self): - with mock.patch.object(promotion, "api", side_effect=[ - {"commit": {"tree": {"sha": self.tree}}}, {"status": "ahead", "files": []}, - {"commit": {"tree": {"sha": "d" * 40}}}, - {"sha": "e" * 40, "commit": {"tree": {"sha": "f" * 40}}}, - ]): - with self.assertRaisesRegex(ValueError, "Main tree differs"): - promotion.verify_landed(self.tree, "a" * 40) - - def test_makefile_allowance_cannot_hide_build_changes(self): - encode = lambda value: promotion.base64.b64encode(value.encode()).decode() - anchor = "\tPYTHONDONTWRITEBYTECODE=1 python3 scripts/core-distribution-manifest.test.py\n" - with mock.patch.object(promotion, "api", side_effect=[ - {"commit": {"tree": {"sha": self.tree}}}, - {"status": "ahead", "files": [{"filename": "Makefile"}]}, - {"content": encode(anchor)}, {"content": encode(anchor + "build:\n\techo changed\n")}, - ]): - with self.assertRaisesRegex(ValueError, "exceeds promotion test"): - promotion.verify_landed(self.tree, "a" * 40) - - def test_unreviewed_package_has_no_remote_side_effects(self): - (self.package / "fixture.py").write_text("changed") - with mock.patch.object(promotion, "gh") as gh, mock.patch.object(promotion, "api") as api: - with self.assertRaisesRegex(ValueError, "bytes/set mismatch"): - promotion.promote(self.assets, self.root / "state", "mx2", "/tmp/acceptance", "d" * 40, **self.kwargs) - gh.assert_not_called() - api.assert_not_called() - self.assertFalse((self.root / "state").exists()) - - def qualify(self, checks=None, failure=False, wrong_identity=False): - adapter = self.root / "adapter.py" - adapter.write_text("# Transport test fixture, never live acceptance\n") - adapter.with_name('qualification_control.py').write_bytes(PathControl.read_bytes()) - request = {"source": promotion.SOURCE, "tree": self.tree, "run_id": "test-run", - "inventory_sha256": "c" * 64, "directory": "/tmp/test", "inventory": {}} - - def transport(argv, request_input=None, **kwargs): - if "exec(compile" in argv[-1]: - if failure: - raise subprocess.CalledProcessError(1, argv) - result = {key: request[key] for key in ( - "source", "tree", "run_id", "inventory_sha256", "adapter_sha256")} - result["status"] = "passed" - result["qualification_manifest_sha256"] = self.manifest_hash - result["checks"] = checks if checks is not None else {name: "passed" for name in promotion.REQUIRED_CHECKS} - if wrong_identity: - result["run_id"] = "previous-run" - return json.dumps(result) - return "" - - with mock.patch.object(promotion, "run", side_effect=transport), \ - mock.patch.object(promotion.qualification_adapter.control, "transport", side_effect=transport): - return promotion.qualification(request, "mx2", "/tmp/test", adapter, self.assets, self.package, self.manifest_hash) - - def test_nonzero_ssh_missing_checks_and_replayed_result_rejected(self): - with self.assertRaises(subprocess.CalledProcessError): - self.qualify(failure=True) - with self.assertRaisesRegex(ValueError, "missing, failed or skipped"): - self.qualify(checks={"fresh-install": "passed"}) - with self.assertRaisesRegex(ValueError, "identity mismatch"): - self.qualify(wrong_identity=True) - - def test_complete_transport_result(self): - self.assertEqual(self.qualify()["checks"], {name: "passed" for name in promotion.REQUIRED_CHECKS}) - - def test_publication_waits_for_live_result_and_merge(self): - for failure in ("qualification", "wait_for_landed"): - with self.subTest(failure=failure): - state = self.root / failure - draft = {"id": 1, "draft": True, "target_commitish": promotion.SOURCE} - ready = json.dumps({"ready": True, "required_checks": list(promotion.REQUIRED_CHECKS)}) - with mock.patch.object(promotion, "run", return_value=ready), \ - mock.patch.object(promotion, "verify_tooling"), \ - mock.patch.object(promotion, "api", return_value={"commit": {"tree": {"sha": self.tree}}}), \ - mock.patch.object(promotion, "verify_tag"), \ - mock.patch.object(promotion, "release_state", return_value=draft), \ - mock.patch.object(promotion, "download"), \ - mock.patch.object(promotion, "verify_files"), \ - mock.patch.object(promotion, "qualification", return_value={}) as qualify, \ - mock.patch.object(promotion, "wait_for_landed") as landed, \ - mock.patch.object(promotion, "gh") as gh: - (qualify if failure == "qualification" else landed).side_effect = ValueError("blocked") - with self.assertRaisesRegex(ValueError, "blocked"): - promotion.promote(self.assets, state, "mx2", "/tmp/acceptance", "d" * 40, **self.kwargs) - gh.assert_not_called() - - def test_success_publishes_only_after_final_download_verification(self): - draft = {"id": 1, "draft": True, "target_commitish": promotion.SOURCE} - final = dict(draft, draft=False, prerelease=False, html_url="https://example.invalid/release") - ready = json.dumps({"ready": True, "required_checks": list(promotion.REQUIRED_CHECKS)}) - events = [] - - def download(path, inventory): - events.append(path.name) - - with mock.patch.object(promotion, "run", return_value=ready), \ - mock.patch.object(promotion, "verify_tooling"), \ - mock.patch.object(promotion, "api", return_value={"commit": {"tree": {"sha": self.tree}}}), \ - mock.patch.object(promotion, "verify_tag"), \ - mock.patch.object(promotion, "release_state", side_effect=[draft, draft, draft, draft, final]), \ - mock.patch.object(promotion, "download", side_effect=download), \ - mock.patch.object(promotion, "verify_files"), \ - mock.patch.object(promotion, "qualification", side_effect=lambda *args: events.append("qualified") or {}), \ - mock.patch.object(promotion, "wait_for_landed", side_effect=lambda *args: events.append("landed")), \ - mock.patch.object(promotion, "verify_landed", side_effect=lambda *args: events.append("rechecked")), \ - mock.patch.object(promotion, "publish_release", side_effect=lambda release_id, body: events.append(("publish", release_id))), \ - mock.patch("builtins.print"): - promotion.promote(self.assets, self.root / "success", "mx2", "/tmp/acceptance", "d" * 40, **self.kwargs) - self.assertEqual(events, ["downloaded", "qualified", "landed", "before-publication", - "rechecked", ("publish", 1), "published"]) - - def test_final_identity_changes_never_publish(self): - draft = {"id": 1, "draft": True, "target_commitish": promotion.SOURCE} - ready = json.dumps({"ready": True, "required_checks": list(promotion.REQUIRED_CHECKS)}) - for change in ("main", "tag", "replacement", "published"): - with self.subTest(change=change): - final = dict(draft) - if change == "replacement": - final["id"] = 2 - if change == "published": - final["draft"] = False - with mock.patch.object(promotion, "run", return_value=ready), \ - mock.patch.object(promotion, "verify_tooling"), \ - mock.patch.object(promotion, "api", return_value={"commit": {"tree": {"sha": self.tree}}}), \ - mock.patch.object(promotion, "verify_tag", side_effect=[None, None, ValueError("changed") if change == "tag" else None]), \ - mock.patch.object(promotion, "release_state", side_effect=[draft, draft, draft, final]), \ - mock.patch.object(promotion, "download") as download, \ - mock.patch.object(promotion, "verify_files"), \ - mock.patch.object(promotion, "qualification", return_value={}), \ - mock.patch.object(promotion, "wait_for_landed"), \ - mock.patch.object(promotion, "verify_landed", side_effect=ValueError("changed") if change == "main" else None), \ - mock.patch.object(promotion, "publish_release") as publish: - with self.assertRaises(ValueError): - promotion.promote(self.assets, self.root / change, "mx2", "/tmp/acceptance", "d" * 40, **self.kwargs) - self.assertEqual(download.call_args_list[-1].args[0].name, "before-publication") - publish.assert_not_called() - - def test_publication_targets_verified_release_id(self): - body = self.root / "publication.json" - body.write_text('{"draft":false}') - with mock.patch.object(promotion, "run", return_value='{"id":37,"draft":false,"prerelease":false}') as run: - promotion.publish_release(37, body) - self.assertEqual(run.call_args.args[0], ["gh", "api", "--method", "PATCH", - "repos/" + promotion.REPO + "/releases/37", "--input", str(body)]) - with mock.patch.object(promotion, "run", return_value='{"id":38,"draft":false,"prerelease":false}'): - with self.assertRaisesRegex(ValueError, "identity mismatch"): - promotion.publish_release(37, body) - - def test_download_hashes_stream_without_storing_an_archive(self): - payload = b"downloaded bytes" - inventory = {"asset.tar.gz": {"sha256": hashlib.sha256(payload).hexdigest(), "size": len(payload)}} - class Process: - def __init__(self, *args, **kwargs): - self.stdout = io.BytesIO(payload) - def __enter__(self): - return self - def __exit__(self, *args): - self.stdout.close() - def wait(self): - return 0 - with mock.patch.object(promotion, "release_state", return_value={"id": 1}), \ - mock.patch.object(promotion, "run", return_value='[[{"name":"asset.tar.gz"}]]'), \ - mock.patch.object(promotion.subprocess, "Popen", Process): - promotion.download(self.root / "streamed", inventory) - self.assertTrue((self.root / "streamed.json").is_file()) - self.assertFalse((self.root / "streamed").exists()) - inventory["asset.tar.gz"]["sha256"] = "0" * 64 - with self.assertRaisesRegex(ValueError, "bytes changed"): - promotion.download(self.root / "bad-stream", inventory) - self.assertFalse((self.root / "bad-stream.json").exists()) - - def test_recorded_pass_cannot_resume_an_interrupted_run(self): - state = self.root / "previous-run" - state.mkdir() - (state / "qualification.json").write_text('{"passed":true}') - ready = json.dumps({"ready": True, "required_checks": list(promotion.REQUIRED_CHECKS)}) - with mock.patch.object(promotion, "run", return_value=ready), \ - mock.patch.object(promotion, "verify_tooling"), mock.patch.object(promotion, "gh") as gh: - with self.assertRaises(FileExistsError): - promotion.promote(self.assets, state, "mx2", "/tmp/acceptance", "d" * 40, **self.kwargs) - gh.assert_not_called() - - -class SupervisionTests(unittest.TestCase): - """Actual short-lived child fixtures exercise supervision, never Core acceptance.""" - def setUp(self): - self.temp = tempfile.TemporaryDirectory() - self.addCleanup(self.temp.cleanup) - self.root = pathlib.Path(self.temp.name) - self.package = self.root / 'tools' - self.package.mkdir() - self.assets = self.root / 'assets' - self.assets.mkdir() - (self.assets / 'fixture.tar.gz').write_bytes(b'not a release') - self.script = self.package / 'fixture.py' - self.script.write_text("""import json,sys -q=json.load(sys.stdin);name=sys.argv[1] -r={k:q[k] for k in ('source','tree','run_id','inventory_sha256','adapter_sha256')} -count=q['owned_resources'].get('child_count',0) -assert (q['previous_stage_result'] is None)==(count==0) -r.update(status='passed',checks={name:'passed'},owned_resources={'child_count':count+1}) -print(json.dumps(r)) -""") - self.manifest = {'version': 1, 'configuration': {}, 'files': {}, 'stages': [ - {'name': name, 'python': sys.executable, 'script': 'fixture.py', 'args': [name], - 'timeout_seconds': 3} for name in promotion.REQUIRED_CHECKS]} - inventory = {'fixture.tar.gz': promotion.file_identity(self.assets / 'fixture.tar.gz')} - self.request = {'source': 'a'*40, 'tree': 'b'*40, 'run_id': 'cccccccc-cccc-4ccc-8ccc-cccccccccccc', - 'directory': str(self.assets), 'inventory': inventory, - 'inventory_sha256': hashlib.sha256(promotion.canonical(inventory).encode()).hexdigest(), - 'adapter_sha256': 'd'*64, 'required_checks': list(promotion.REQUIRED_CHECKS), - 'qualification_package': str(self.package)} - self.freeze() - - def freeze(self): - self.manifest['files'] = {'fixture.py': promotion.file_identity(self.script)} - path = self.package / 'manifest.json' - path.write_text(promotion.canonical(self.manifest)) - self.request['qualification_manifest_sha256'] = promotion.file_identity(path)['sha256'] - - def test_required_actual_children_and_exclusive_supervision(self): - result = promotion.qualification_adapter.qualify(self.request) - self.assertEqual(result['owned_resources'], {'child_count': len(promotion.REQUIRED_CHECKS)}) - self.assertEqual(result['checks'], {name: 'passed' for name in promotion.REQUIRED_CHECKS}) - with self.assertRaises(FileExistsError): - promotion.qualification_adapter.qualify(self.request) - - def test_package_mutation_and_unlisted_file_rejected(self): - self.script.write_text('# changed') - with self.assertRaisesRegex(ValueError, 'bytes/set mismatch'): - promotion.qualification_adapter.qualify(self.request) - self.freeze() - (self.package / 'pass.json').write_text('{"passed":true}') - with self.assertRaisesRegex(ValueError, 'bytes/set mismatch'): - promotion.qualification_adapter.qualify(self.request) - self.assertFalse((self.assets / 'qualification').exists()) - - def test_old_pass_file_cannot_replace_failed_child(self): - self.script.write_text("import sys;sys.exit(9)\n") - self.freeze() - (self.assets / 'old-pass.json').write_text('{"status":"passed"}') - with self.assertRaisesRegex(ValueError, 'child failed'): - promotion.qualification_adapter.qualify(self.request) - self.assertFalse((self.assets / 'qualification/supervision.result.json').exists()) - - def test_wrong_identity_and_extra_passed_check_rejected(self): - self.script.write_text(self.script.read_text().replace("print(json.dumps(r))", "r['checks']['invented']='passed';print(json.dumps(r))")) - self.freeze() - with self.assertRaisesRegex(ValueError, 'identity or required check'): - promotion.qualification_adapter.qualify(self.request) - - def test_child_timeout_never_completes_qualification(self): - self.script.write_text('import time;time.sleep(20)\n') - self.manifest['stages'][0]['timeout_seconds'] = 1 - self.freeze() - with self.assertRaises(subprocess.TimeoutExpired): - promotion.qualification_adapter.qualify(self.request) - self.assertFalse((self.assets / 'qualification/supervision.result.json').exists()) - - def test_structured_commands_reject_traversal_and_missing_stage(self): - for change in ('path', 'stage'): - if change == 'path': - self.manifest['stages'][0]['script'] = '../fixture.py' - else: - self.manifest['stages'] = self.manifest['stages'][:-1] - self.freeze() - with self.subTest(change=change), self.assertRaises(ValueError): - promotion.qualification_adapter.qualify(self.request) - - def test_merge_wait_stays_in_process_without_requalification(self): - with mock.patch.object(promotion, 'verify_landed', side_effect=[False, False, True]) as landed, \ - mock.patch.object(promotion.time, 'monotonic', side_effect=[0, 1, 2]), \ - mock.patch.object(promotion.time, 'sleep') as sleep, \ - mock.patch.object(promotion, 'qualification') as qualify: - promotion.wait_for_landed('b'*40, 'd'*40, 60) - self.assertEqual(landed.call_count, 3) - self.assertEqual(sleep.call_count, 2) - qualify.assert_not_called() - - def test_only_ancestor_main_waits_for_exact_reviewed_tree(self): - promotion.select_source('a'*40) - prefix=[{'commit':{'tree':{'sha':'b'*40}}}, {'status':'ahead','files':[]}, - {'commit':{'tree':{'sha':'d'*40}}}, {'sha':'e'*40,'commit':{'tree':{'sha':'f'*40}}}] - with mock.patch.object(promotion,'api',side_effect=prefix+[{'status':'ahead'}]): - self.assertIs(promotion.verify_landed('b'*40,'c'*40,allow_pending=True),False) - with mock.patch.object(promotion,'api',side_effect=prefix+[{'status':'diverged'}]): - with self.assertRaisesRegex(ValueError,'Main tree differs'): - promotion.verify_landed('b'*40,'c'*40,allow_pending=True) - - def test_merge_timeout_and_conflict_do_not_publish(self): - with mock.patch.object(promotion, 'verify_landed', return_value=False), \ - mock.patch.object(promotion.time, 'monotonic', side_effect=[0, 61]), \ - mock.patch.object(promotion, 'gh') as gh: - with self.assertRaises(TimeoutError): - promotion.wait_for_landed('b'*40, 'd'*40, 60) - gh.assert_not_called() - with mock.patch.object(promotion, 'verify_landed', side_effect=ValueError('conflict')), \ - mock.patch.object(promotion.time, 'sleep') as sleep: - with self.assertRaisesRegex(ValueError, 'conflict'): - promotion.wait_for_landed('b'*40, 'd'*40, 60) - sleep.assert_not_called() - - def test_candidate_source_is_explicit_and_strict(self): - for source in ('latest', '', 'a'*39, 'A'*40): - with self.subTest(source=source), self.assertRaises(ValueError): - promotion.select_source(source) - promotion.select_source('f'*40) - self.assertEqual(promotion.TAG, 'build-'+'f'*40) - self.assertIn('f'*40, promotion.BASE) - - -if __name__ == "__main__": - unittest.main() diff --git a/scripts/qualification-control.test.py b/scripts/qualification-control.test.py deleted file mode 100644 index d525a89bf..000000000 --- a/scripts/qualification-control.test.py +++ /dev/null @@ -1,161 +0,0 @@ -"""Real child/process control fixtures; no deployment, provider or model calls.""" -import hashlib -import json -import os -from pathlib import Path -import signal -import subprocess -import sys -import tempfile -import time -import unittest -from unittest import mock -from concurrent.futures import ThreadPoolExecutor -import qualification_control as control - - -def alive(pid): - try: - return (Path('/proc')/str(pid)/'stat').read_text().rsplit(')',1)[1].split()[0] != 'Z' - except (FileNotFoundError, ProcessLookupError): - return False - - -class ControlTests(unittest.TestCase): - def setUp(self): - self.temp=tempfile.TemporaryDirectory();self.addCleanup(self.temp.cleanup) - self.root=Path(self.temp.name) - self.source=Path(control.__file__).read_bytes() - self.digest=hashlib.sha256(self.source).hexdigest() - - def receiver(self,worker): - return control.inline_receiver(self.source,self.digest,[sys.executable,'-B','-c',worker]) - - def await_file(self,path): - deadline=time.monotonic()+5 - while not path.exists() and time.monotonic() lifecycle -> installer. - # The outside control owner survives the inner timeout/SIGKILL. - worker = ("import subprocess,sys,time,json;from pathlib import Path;" - "bg=subprocess.Popen([sys.executable,'-c','import time;time.sleep(60)'],start_new_session=True);" - "Path(" + repr(str(background)) + ").write_text(json.dumps({'background':bg.pid}));") - if reason == 'timeout': - worker += "subprocess.run([sys.executable,'-c'," + repr(middle) + "],timeout=.3)" - else: - worker += ("p=subprocess.Popen([sys.executable,'-c'," + repr(middle) + "]);" - "time.sleep(.3);") - if reason == 'kill': - worker += "p.kill();p.wait();sys.exit(1)" - elif reason == 'nonzero': - worker += "sys.exit(7)" - else: - worker += "print(json.dumps({'completed':True}))" - bg = None - try: - with tempfile.TemporaryFile() as out, tempfile.TemporaryFile() as err: - if reason == 'normal': - control.run_child([sys.executable,'-c',worker], b'', out, err, 3) - else: - with self.assertRaises(ValueError): - control.run_child([sys.executable,'-c',worker], b'', out, err, 3) - fg = self.await_file(ready) - bg = self.await_file(background)['background'] - self.assert_stopped(fg) - self.assertTrue(alive(bg), 'Explicit background resource was removed') - finally: - if bg is None and background.exists(): - bg = json.loads(background.read_text())['background'] - if bg is not None: - try: os.kill(bg, signal.SIGKILL) - except ProcessLookupError: pass - - def test_sender_child_cleanup_without_linux_proc(self): - process = subprocess.Popen([sys.executable, '-c', 'import time;time.sleep(60)']) - try: - with mock.patch.object(control, 'descendants', return_value={}): - control.stop_child(process, own_group=False) - self.assertIsNotNone(process.returncode) - finally: - if process.poll() is None: - process.kill(); process.wait() - - def test_control_bytes_are_pinned(self): - with self.assertRaises(ValueError):control.inline_receiver(self.source,'0'*64,['python3']) - - -if __name__=='__main__':unittest.main() diff --git a/scripts/qualification_control.py b/scripts/qualification_control.py deleted file mode 100644 index f87c5caec..000000000 --- a/scripts/qualification_control.py +++ /dev/null @@ -1,224 +0,0 @@ -"""Short-lived SSH control channel; EOF stops future work, never replays writes.""" -from contextlib import contextmanager -import json -import os -from pathlib import Path -import select -import signal -import subprocess -import tempfile -import threading -import time - - -def cancelled(signum, frame): - raise InterruptedError('Qualification control was interrupted') - - -@contextmanager -def cancellable(): - if threading.current_thread() is not threading.main_thread(): - # The process's main supervisor owns signals; pipe EOF still cancels peers. - yield - return - previous = {sig: signal.getsignal(sig) for sig in (signal.SIGTERM, signal.SIGHUP)} - for sig in previous: - signal.signal(sig, cancelled) - try: - yield - finally: - for sig, handler in previous.items(): - signal.signal(sig, handler) - - -def descendants(pid): - """Capture owned Linux descendants before any parent is terminated.""" - processes = {} - for path in Path('/proc').glob('[0-9]*'): - try: - fields = (path / 'stat').read_text().rsplit(')', 1)[1].split() - processes[int(path.name)] = (int(fields[1]), fields[19]) - except (OSError, ValueError, IndexError): - continue - owned = {pid} - while True: - children = {child for child, (parent, _) in processes.items() if parent in owned} - if children <= owned: - return {child: processes[child][1] for child in owned if child in processes} - owned.update(children) - - -def stop_child(process, *, own_group=True): - """Reap foreground work; explicit detached background resources are retained.""" - if own_group: - def send(sig): - try: - os.killpg(process.pid, sig) - except ProcessLookupError: - pass - else: - # Nested commands inherit their enclosing foreground group. Never kill - # that whole group here: its outside owner performs final group cleanup. - owned = descendants(process.pid) - group = os.getpgrp() - def send(sig): - # The top-level sender can run on macOS without /proc. Its direct - # SSH child is still owned and must always receive cancellation. - if process.poll() is None: - process.send_signal(sig) - for pid, birth in owned.items(): - try: - actual = (Path('/proc') / str(pid) / 'stat').read_text().rsplit(')', 1)[1].split()[19] - if actual == birth and os.getpgid(pid) == group: - os.kill(pid, sig) - except (OSError, IndexError): - pass - send(signal.SIGTERM) - try: - process.wait(timeout=10) - except subprocess.TimeoutExpired: - pass - send(signal.SIGKILL) - process.wait() - - -def run_child(argv, payload, stdout, stderr, timeout, **kwargs): - """Outside owner for one foreground group, cleaned on every exit path.""" - with subprocess.Popen(argv, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr, - start_new_session=True, **kwargs) as process: - try: - process.communicate(payload, timeout=timeout) - if process.returncode: - raise ValueError('Qualification child failed; retain private receipts') - finally: - # The direct child may already be gone. Its foreground descendants - # remain addressable by group even after timeout or SIGKILL orphaning. - stop_child(process) - - -class Lifeline: - def __init__(self, stream, timeout=30): - self.fd, self.timeout = stream.fileno(), timeout - self.buffer = b'' - self.stopped = threading.Event() - - def line(self, timeout, limit): - deadline = time.monotonic() + timeout - while not self.stopped.is_set(): - if b'\n' in self.buffer: - line, self.buffer = self.buffer.split(b'\n', 1) - if len(line) > limit: - raise ValueError('Oversized control frame') - return line - if len(self.buffer) > limit or time.monotonic() >= deadline: - raise ValueError('Control channel expired') - if select.select([self.fd], [], [], min(0.2, max(0, deadline-time.monotonic())))[0]: - chunk = os.read(self.fd, 65536) - if not chunk: - raise ValueError('Control channel closed') - self.buffer += chunk - return None - - def watch(self): - try: - while not self.stopped.is_set(): - frame = self.line(self.timeout, 16) - if frame is not None and frame != b'ping': - raise ValueError('Invalid control heartbeat') - except Exception: - if not self.stopped.is_set(): - os.kill(os.getpid(), signal.SIGTERM) - - def start(self): - self.thread = threading.Thread(target=self.watch, daemon=True) - self.thread.start() - - def close(self): - self.stopped.set() - self.thread.join(timeout=1) - - -def serve(callback, stream, timeout=30): - """Read one JSON line, then require heartbeats until the callback finishes.""" - with cancellable(): - life = Lifeline(stream, timeout) - request = json.loads(life.line(60, 4*1024*1024)) - life.start() - try: - return callback(request) - finally: - life.close() - - -def transport(argv, request, timeout=86400): - """Keep the same SSH stdin open; caller death/connection loss is remote EOF.""" - payload = json.dumps(request, sort_keys=True, separators=(',', ':')).encode()+b'\n' - with cancellable(), tempfile.TemporaryFile() as output, tempfile.TemporaryFile() as errors: - with subprocess.Popen(argv, stdin=subprocess.PIPE, stdout=output, stderr=errors, - start_new_session=False) as process: - try: - process.stdin.write(payload) - process.stdin.flush() - deadline = time.monotonic()+timeout - while True: - try: - process.wait(timeout=min(5, max(0, deadline-time.monotonic()))) - break - except subprocess.TimeoutExpired: - if time.monotonic() >= deadline: - raise TimeoutError('Qualification transport expired') - process.stdin.write(b'ping\n') - process.stdin.flush() - if process.returncode: - raise ValueError('Qualification transport failed; retain remote intents') - except BaseException: - try: - process.stdin.close() # Remote EOF stops any later stage. - except OSError: - pass - stop_child(process, own_group=False) - raise - finally: - try: - process.stdin.close() - except OSError: - pass - output.seek(0) - result = output.read(8*1024*1024+1) - if len(result) > 8*1024*1024: - raise ValueError('Oversized qualification result') - return result.decode() - - -def worker(argv, request): - """Wrap a nested worker in the same finite channel and owned-child cleanup.""" - with tempfile.TemporaryFile() as output, tempfile.TemporaryFile() as errors: - run_child(argv, json.dumps(request).encode(), output, errors, 14400) - output.seek(0) - raw = output.read(8*1024*1024+1) - if len(raw) > 8*1024*1024: - raise ValueError('Oversized remote worker result') - return json.loads(raw) - - -def inline_receiver(source, expected_hash, argv): - """Verified control bytes travel with the existing SSH command, without keys.""" - import base64 - import hashlib - if hashlib.sha256(source).hexdigest() != expected_hash: - raise ValueError('Control helper bytes changed') - code = ("import base64,hashlib,os,pathlib,sys; b=base64.b64decode(sys.argv[1]); " - "assert hashlib.sha256(b).hexdigest()==sys.argv[2]; os.chdir(pathlib.Path.home()); " - "sys.argv=['qualification_control.py','--receive']+sys.argv[3:]; " - "exec(compile(b,'qualification_control.py','exec'),{'__name__':'__main__','__file__':'qualification_control.py'})") - return ['python3', '-B', '-c', code, base64.b64encode(source).decode(), expected_hash, *argv] - - -if __name__ == '__main__': - import sys - if sys.argv[1:2] != ['--receive'] or len(sys.argv) < 3: - raise SystemExit('A supervised worker command is required') - try: - print(json.dumps(serve(lambda request: worker(sys.argv[2:], request), sys.stdin))) - except Exception: - raise SystemExit('Remote control ended; retain unknown intents and owned resources. No replay.') from None diff --git a/scripts/qualify-core-release.py b/scripts/qualify-core-release.py deleted file mode 100644 index 70ed3b6a9..000000000 --- a/scripts/qualify-core-release.py +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/env python3 -"""Execute a maintainer-pinned private qualification package over authenticated SSH. - -The package is independent of candidate assets. Its manifest fixes all helper -bytes, structured Python commands, timeouts and private path/resource configuration. -Only fresh child exit status and identity-bound stdout establish stage completion. -""" -import hashlib -import importlib.util -import json -import os -from pathlib import Path, PurePosixPath -import re -import signal -import subprocess -import sys -import uuid - -if 'qualification_control' in sys.modules: - control = sys.modules['qualification_control'] -else: - _control_spec = importlib.util.spec_from_file_location( - 'qualification_control', Path(__file__).with_name('qualification_control.py')) - control = importlib.util.module_from_spec(_control_spec) - _control_spec.loader.exec_module(control) - -CHECKS = ('fresh-install', 'current-lifecycle', 'managed-native-smoke', - 'diagnostics-observations-smoke', 'node-runtime-smoke') -IDENTITY = ('source', 'tree', 'run_id', 'inventory_sha256', 'adapter_sha256') - - -def canonical(value): - return json.dumps(value, sort_keys=True, separators=(',', ':')).encode() - - -def file_identity(path): - if path.is_symlink() or not path.is_file(): - raise ValueError('Expected a regular package file') - checksum = hashlib.sha256() - with path.open('rb') as stream: - for chunk in iter(lambda: stream.read(1024 * 1024), b''): - checksum.update(chunk) - return {'sha256': checksum.hexdigest(), 'size': path.stat().st_size} - - -def relative_path(name): - if (not isinstance(name, str) or not re.fullmatch(r'[A-Za-z0-9_./-]+', name) - or PurePosixPath(name).is_absolute() or any(p in ('', '.', '..') for p in name.split('/'))): - raise ValueError('Invalid package path') - return name - - -def verify_package(root, expected_hash): - if root.is_symlink() or not root.is_dir() or not re.fullmatch('[0-9a-f]{64}', expected_hash): - raise ValueError('Explicit qualification package and manifest SHA256 required') - manifest_path = root / 'manifest.json' - if file_identity(manifest_path)['sha256'] != expected_hash: - raise ValueError('Qualification manifest hash mismatch') - manifest = json.loads(manifest_path.read_bytes()) - if (set(manifest) != {'version', 'files', 'stages', 'configuration'} or manifest['version'] != 1 - or not isinstance(manifest['configuration'], dict) or not isinstance(manifest['files'], dict)): - raise ValueError('Invalid qualification package manifest') - expected = manifest['files'] - for name, identity in expected.items(): - relative_path(name) - if name == 'manifest.json' or not isinstance(identity, dict) or set(identity) != {'sha256', 'size'}: - raise ValueError('Invalid package file identity') - if (not re.fullmatch('[0-9a-f]{64}', identity.get('sha256', '')) - or type(identity.get('size')) is not int or identity['size'] < 0): - raise ValueError('Invalid package file digest or size') - actual = {} - for path in root.rglob('*'): - if path.is_symlink(): - raise ValueError('Package symlinks are forbidden') - if path.is_dir(): - continue - name = path.relative_to(root).as_posix() - if name != 'manifest.json': - actual[name] = file_identity(path) - if actual != expected: - raise ValueError('Qualification package bytes/set mismatch') - stages = manifest['stages'] - if not isinstance(stages, list) or [stage.get('name') for stage in stages] != list(CHECKS): - raise ValueError('Exact ordered qualification stages required') - for stage in stages: - if set(stage) != {'name', 'python', 'script', 'args', 'timeout_seconds'}: - raise ValueError('Invalid stage command fields') - if (not isinstance(stage['python'], str) or not re.fullmatch(r'/[A-Za-z0-9_./-]+', stage['python']) - or '..' in PurePosixPath(stage['python']).parts - or relative_path(stage['script']) not in expected or not stage['script'].endswith('.py') - or not isinstance(stage['args'], list) - or any(not isinstance(arg, str) or '\x00' in arg for arg in stage['args']) - or type(stage['timeout_seconds']) is not int or not 1 <= stage['timeout_seconds'] <= 14400): - raise ValueError('Invalid bounded structured Python command') - return manifest - - -def verify_assets(request): - if (any(not re.fullmatch('[0-9a-f]{40}', request.get(key, '')) for key in ('source', 'tree')) - or str(uuid.UUID(request.get('run_id', ''))) != request.get('run_id') - or request.get('required_checks') != list(CHECKS)): - raise ValueError('Invalid qualification identity') - for key in ('inventory_sha256', 'adapter_sha256'): - if not re.fullmatch('[0-9a-f]{64}', request.get(key, '')): - raise ValueError('Missing qualification identity digest') - if hashlib.sha256(canonical(request['inventory'])).hexdigest() != request['inventory_sha256']: - raise ValueError('Inventory digest mismatch') - root = Path(request['directory']) - if not root.is_absolute() or root.resolve() != root: - raise ValueError('Invalid candidate directory') - for name, expected in request['inventory'].items(): - if relative_path(name) != Path(name).name or file_identity(root / name) != expected: - raise ValueError('Candidate bytes changed') - - -def save_new(path, value): - with path.open('x', encoding='utf8') as stream: - json.dump(value, stream, sort_keys=True) - stream.flush() - os.fsync(stream.fileno()) - - -def execute_stage(stage, package, request, evidence, script_hash): - # Execute the verified script bytes; helper imports are checked as a full set - # before and after each stage. The authorized host remains the trust boundary. - script = package / stage['script'] - code = ("import hashlib,pathlib,sys; p=pathlib.Path(sys.argv[1]); b=p.read_bytes(); " - "hashlib.sha256(b).hexdigest()==sys.argv[2] or sys.exit('Stage bytes changed'); " - "sys.path.insert(0,str(p.parent)); sys.argv=[str(p)]+sys.argv[3:]; " - "exec(compile(b,str(p),'exec'),{'__name__':'__main__','__file__':str(p)})") - argv = [stage['python'], '-B', '-c', code, str(script), script_hash, *stage['args']] - env = dict(os.environ, PYTHONDONTWRITEBYTECODE='1', PYTHONPATH=str(package)) - output = evidence / (stage['name'] + '.stdout.private.json') - with output.open('xb') as stdout, (evidence / (stage['name'] + '.stderr.private.log')).open('xb') as stderr: - control.run_child(argv, canonical(request), stdout, stderr, stage['timeout_seconds'], - cwd=package, env=env) - if output.stat().st_size > 8 * 1024 * 1024: - raise ValueError('Oversized stage result') - result = json.loads(output.read_bytes()) - if (not isinstance(result, dict) or any(result.get(key) != request[key] for key in IDENTITY) - or result.get('status') != 'passed' or result.get('checks') != {stage['name']: 'passed'} - or not isinstance(result.get('owned_resources'), dict)): - raise ValueError('Stage result identity or required check mismatch') - return result - - -def qualify(request): - os.umask(0o077) - verify_assets(request) - package = Path(request['qualification_package']) - if not package.is_absolute() or package.resolve() != package: - raise ValueError('Invalid qualification package directory') - manifest = verify_package(package, request['qualification_manifest_sha256']) - evidence = Path(request['directory']) / 'qualification' - evidence.mkdir(mode=0o700) # Never read or resume an old passed result. - save_new(evidence / 'supervision.intent.json', {key: request[key] for key in IDENTITY}) - owned, previous, completed = {}, None, {} - for stage in manifest['stages']: - verify_assets(request) - if verify_package(package, request['qualification_manifest_sha256']) != manifest: - raise ValueError('Qualification package changed') - child = dict(request, owned_resources=owned, previous_stage_result=previous, - package_configuration=manifest['configuration']) - result = execute_stage(stage, package, child, evidence, manifest['files'][stage['script']]['sha256']) - verify_package(package, request['qualification_manifest_sha256']) - verify_assets(request) - owned, previous = result['owned_resources'], result - completed.update(result['checks']) - result = {key: request[key] for key in IDENTITY} - result.update(status='passed', checks=completed, owned_resources=owned, - qualification_manifest_sha256=request['qualification_manifest_sha256']) - save_new(evidence / 'supervision.result.json', result) - return result - - -def main(): - if sys.argv[1:] == ['--describe']: - print(json.dumps({'ready': True, 'required_checks': list(CHECKS)})) - return 0 - try: - print(json.dumps(control.serve(qualify, sys.stdin), sort_keys=True)) - return 0 - except Exception: - # Native output, credentials and private paths stay in private receipts. - print('Qualification stopped; retain owned resources and private receipts.', file=sys.stderr) - return 1 - - -if __name__ == '__main__': - sys.exit(main()) From 6af352a30d33c260300367957aefbe6800b04047 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 08:30:14 +0000 Subject: [PATCH 4/9] chore: delete the standalone Core archive No workflow built it and no Release carried it; it repackaged the make build-core commands. Remove build-core-release.sh, its Make target, the archive README and the documentation that described it. --- Makefile | 5 +- docs/maintainers.md | 5 +- scripts/build-core-release.sh | 115 ---------------------------------- services/core/README.md | 2 - services/core/RELEASE.md | 47 -------------- 5 files changed, 2 insertions(+), 172 deletions(-) delete mode 100755 scripts/build-core-release.sh delete mode 100644 services/core/RELEASE.md diff --git a/Makefile b/Makefile index 8fea3c0e4..929c1b097 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ SQLC_VERSION ?= v1.29.0 SQLC ?= go run github.com/sqlc-dev/sqlc/cmd/sqlc@$(SQLC_VERSION) SWAG_VERSION ?= v1.16.4 -.PHONY: help check check-database check-go check-sqlc sqlc-generate node-deps check-claude-sdk check-web check-mcode-harness build-daemon build-core build-core-release check-core docker-build-core check-core-container build-agents-runtime build-claude-runtime build-claude-sdk-runtime build-mcode-harness build-mcode-runtime +.PHONY: help check check-database check-go check-sqlc sqlc-generate node-deps check-claude-sdk check-web check-mcode-harness build-daemon build-core check-core docker-build-core check-core-container build-agents-runtime build-claude-runtime build-claude-sdk-runtime build-mcode-harness build-mcode-runtime help: @printf '%s\n' 'make build-core Build standalone Core commands' 'make build-daemon Build the execution daemon' 'make check Run Core, persistence and runtime checks' 'See README.md for runtime prerequisites and deployment.' @@ -67,9 +67,6 @@ build-daemon: build-core: ./scripts/build-core.sh -build-core-release: - ./scripts/build-core-release.sh - check-core: build-core # Persistence integration tests include bounded lifecycle waits that together exceed Go's 10m default. go test ./services/core/... ./packages/agents-client/... -count=1 -timeout=20m diff --git a/docs/maintainers.md b/docs/maintainers.md index 21720481d..2c756747e 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -111,8 +111,6 @@ The helper is written to `~/.oac/build/microsandbox-provider/oac-microsandbox-pr `make docker-build-core` builds the image `oac-core:dev` (`OAC_DEV_CORE_IMAGE` selects another name) from those five commands and the E2B helper. The base is the digest-pinned `debian:bookworm-slim` with CA certificates and the glibc runtime the helper needs; the default user is UID/GID 65532 and Core listens on `:8091`. The image is Linux amd64 only and is not pushed to a registry. Changes to the image or its build need `make check-core-container` in addition to `make check`: it runs the official-client suite against the image with a read-only root filesystem and needs Linux Docker, a non-root user, and the [test database and pinned SDK](../services/core/README.md#official-client-verification) of the service checks (`OAC_TEST_DATABASE_URL` naming an `oac_*_tests` database with the migrations applied, and `OAC_TEST_OFFICIAL_SDK_PYTHON`). -`make build-core-release` packages the same five commands into `oac-core--linux-amd64.tar.gz` and its `.sha256` under `~/.oac/build/oac-core-release` (`OAC_DEV_RELEASE_DIR`). Beside `bin/`, the archive holds the [archive README](../services/core/RELEASE.md), the license, `manifest.json` (commit, tree, platform, Go version, upstream protocol and binary hashes) and `SHA256SUMS`, which lists every packaged file. The build needs clean committed source and Python 3.9 or newer, and packages deterministically. It carries no configuration, credentials, Web or Runtime. Test archive changes by extracting a fresh copy and running its commands. - ## Publish a version Push a version tag on the reviewed commit to run the `core-release` workflow: @@ -180,9 +178,8 @@ gh variable set OAC_USE_GITHUB_RUNNERS --body true --repo MiniMax-AI/OpenAgentCo This is an explicit operator switch, not an automatic billing balance probe. Runner selection applies to newly scheduled runs. Check current allowance and platform conversion rates in [Blacksmith's runner documentation](https://docs.blacksmith.sh/blacksmith-runners/overview) before treating 2-vCPU usage as free; Windows minutes consume more allowance than Linux minutes. Standard GitHub runner usage follows the repository's visibility and GitHub plan. These workflows request no Blacksmith runner larger than 2 vCPU and no paid cache add-on. ## Run Core without the installer -The standalone archive and container give you Core alone: no Web, no `oac` command and no `config.json`. They suit development, testing and operators who supervise Core themselves. Core reads only its environment; the [configuration appendix](configuration.md#appendix-core-environment-without-the-installer) lists the variables. `OAC_DATABASE_URL` and `OAC_CORE_KEY_DIGESTS_FILE` are required; set `OAC_PUBLIC_URL` to the origin machines use to reach Core, or Core runs without the daemon transport. +The standalone container gives you Core alone: no Web, no `oac` command and no `config.json`. It suits development, testing and operators who supervise Core themselves. Core reads only its environment; the [configuration appendix](configuration.md#appendix-core-environment-without-the-installer) lists the variables. `OAC_DATABASE_URL` and `OAC_CORE_KEY_DIGESTS_FILE` are required; set `OAC_PUBLIC_URL` to the origin machines use to reach Core, or Core runs without the daemon transport. -- The [archive README](../services/core/RELEASE.md) covers the standalone archive. - The [service guide](../services/core/README.md) covers building and running Core from source. To run the container, create a private directory (mode 0700) with `api.env` (`OAC_DATABASE_URL` for a dedicated database, reachable from the container, `OAC_CORE_KEY_DIGESTS_FILE=/run/core-key-digests.json`, and `OAC_PUBLIC_URL`) and `core-key-digests.json`, a JSON array with the lowercase hex SHA-256 digest of your Core key. Keep both files mode 0600 and the Core key itself elsewhere. Run the migrations, then start Core: diff --git a/scripts/build-core-release.sh b/scripts/build-core-release.sh deleted file mode 100755 index 63b4b5356..000000000 --- a/scripts/build-core-release.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -runtime_root="${OAC_DEV_HOME:-$HOME/.oac}" -output_dir="${OAC_DEV_RELEASE_DIR:-$runtime_root/build/oac-core-release}" -export GOCACHE="${GOCACHE:-$runtime_root/cache/go-build}" -export GOMODCACHE="${GOMODCACHE:-$runtime_root/cache/go-mod}" -python3 - "$HOME/.oac" "$runtime_root" "$output_dir" "$GOCACHE" "$GOMODCACHE" <<'PY' -import pathlib -import sys - -if sys.version_info < (3, 9): - sys.exit("Agents API releases require Python 3.9 or newer") -base = pathlib.Path(sys.argv[1]).resolve() -for value in sys.argv[2:]: - path = pathlib.Path(value) - if not path.is_absolute() or not path.resolve().is_relative_to(base): - sys.exit("Agents API release directories must be absolute and under ~/.oac") -PY - -require_clean_source() { - local source_status - source_status="$(git -C "$repo_root" status --porcelain --untracked-files=all)" - if [[ -n "$source_status" ]]; then - printf 'Agents API releases require a clean, committed source tree\n' >&2 - exit 1 - fi -} -require_clean_source -source_revision="$(git -C "$repo_root" rev-parse HEAD)" -source_tree="$(git -C "$repo_root" rev-parse "$source_revision^{tree}")" -source_epoch="$(git -C "$repo_root" show -s --format=%ct "$source_revision")" -archive_name="oac-core-$source_revision-linux-amd64.tar.gz" - -mkdir -p "$output_dir" -release_context="$(mktemp -d "$output_dir/.staging.XXXXXX")" -trap 'rm -rf "$release_context"' EXIT -mkdir -p "$release_context/source" "$release_context/package/bin" "$release_context/go-tmp" -export GOTMPDIR="$release_context/go-tmp" -# Build committed bytes so ignored files or concurrent edits cannot change provenance. -git -C "$repo_root" archive "$source_revision" | tar -C "$release_context/source" -xf - -export GOOS=linux GOARCH=amd64 GOAMD64=v1 GOTOOLCHAIN=local -go_version="$(go env GOVERSION)" -required_go="$(awk '$1 == "go" { print "go" $2; exit }' "$release_context/source/go.mod")" -if [[ "$go_version" != "$required_go" ]]; then - printf 'Agents API release requires %s; found %s\n' "$required_go" "$go_version" >&2 - exit 1 -fi -OAC_DEV_BUILD_REVISION="$source_revision" OAC_DEV_CORE_BUILD_DIR="$release_context/package/bin" \ - "$release_context/source/scripts/build-core.sh" -require_clean_source -if [[ "$(git -C "$repo_root" rev-parse HEAD)" != "$source_revision" ]]; then - printf 'Agents API source changed during release build\n' >&2 - exit 1 -fi - -python3 - "$release_context" "$source_revision" "$source_tree" "$source_epoch" "$go_version" "$archive_name" <<'PY' -import gzip -import hashlib -import json -import pathlib -import sys -import tarfile - -root = pathlib.Path(sys.argv[1]) -revision, tree, epoch, go_version, archive_name = sys.argv[2:] -source, package = root / "source", root / "package" -binaries = ["oac-core", "oac-core-migrate", "oac-core-device", "oac-core-environment-key", "oac-node"] - - -def sha256(path): - digest = hashlib.sha256() - with path.open("rb") as stream: - for block in iter(lambda: stream.read(1024 * 1024), b""): - digest.update(block) - return digest.hexdigest() - - -readme = (source / "services/core/RELEASE.md").read_text(encoding="utf-8") -if "@SOURCE_REVISION@" not in readme: - sys.exit("Agents API RELEASE.md must link to @SOURCE_REVISION@") -readme = readme.replace("@SOURCE_REVISION@", revision).replace("@ARCHIVE_NAME@", archive_name.removesuffix(".tar.gz")) -(package / "README.md").write_text(readme, encoding="utf-8") -(package / "LICENSE").write_bytes((source / "LICENSE").read_bytes()) -manifest = { - "artifact": "oac-core", - "source": {"commit": revision, "tree": tree, "commit_timestamp": int(epoch)}, - "platform": {"os": "linux", "architecture": "amd64", "goamd64": "v1"}, - "go_version": go_version, - "upstream_protocol": json.loads((source / "contracts/agents-api/upstream.json").read_text(encoding="utf-8")), - "binaries": {"bin/" + name: {"sha256": sha256(package / "bin" / name)} for name in binaries}, -} -(package / "manifest.json").write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8") -members = sorted(["bin/" + name for name in binaries] + ["LICENSE", "README.md", "manifest.json"]) -(package / "SHA256SUMS").write_text("".join(sha256(package / name) + " " + name + "\n" for name in members), encoding="utf-8") -members = sorted(members + ["SHA256SUMS"]) -prefix = archive_name.removesuffix(".tar.gz") -archive = root / archive_name -with archive.open("wb") as output: - with gzip.GzipFile(filename="", mode="wb", fileobj=output, mtime=0, compresslevel=9) as compressed: - with tarfile.open(fileobj=compressed, mode="w", format=tarfile.USTAR_FORMAT) as bundle: - for name in members: - path = package / name - info = tarfile.TarInfo(prefix + "/" + name) - info.size = path.stat().st_size - info.mode = 0o755 if name.startswith("bin/") else 0o644 - info.mtime = int(epoch) - with path.open("rb") as contents: - bundle.addfile(info, contents) -(root / (archive_name + ".sha256")).write_text(sha256(archive) + " " + archive_name + "\n", encoding="utf-8") -PY - -mv -f "$release_context/$archive_name" "$release_context/$archive_name.sha256" "$output_dir/" -printf 'Standalone Agents API release: %s/%s\n' "$output_dir" "$archive_name" diff --git a/services/core/README.md b/services/core/README.md index 0ca8f7b67..2e33d2bcb 100644 --- a/services/core/README.md +++ b/services/core/README.md @@ -80,8 +80,6 @@ configuration and a separately installed execution daemon are still required; these binaries do not establish full protocol coverage. For a standalone Linux container, see [Run Core without the installer](../../docs/maintainers.md#run-core-without-the-installer). -`make build-core-release` packages these commands and `oac-node` in a versioned Linux amd64 archive, with source/protocol identity, checksums, a license and [operator instructions](RELEASE.md). Build from a clean Git worktree with Go and Python 3.9+; output defaults to `~/.oac/build/oac-core-release` (or `OAC_DEV_RELEASE_DIR`). The extracted API needs no source checkout or compiler. The archive and the container are advanced paths for running Core alone; see [Maintainers and advanced deployments](../../docs/maintainers.md). Docker-hosted deployments use the Core distribution and its [installer](../../docs/getting-started/install.md), whose manifest carries the complete release; Docker nodes are added from Web. - ## Database ownership Use a dedicated PostgreSQL database and account, separate from the Parsar product. diff --git a/services/core/RELEASE.md b/services/core/RELEASE.md deleted file mode 100644 index e1faa8a7c..000000000 --- a/services/core/RELEASE.md +++ /dev/null @@ -1,47 +0,0 @@ -# Standalone Core archive - -This Linux amd64 archive holds Core alone: the API server `oac-core`, its migrator `oac-core-migrate`, and the operator commands `oac-core-device`, `oac-core-environment-key` and `oac-node`. It has no Web console, installer or `oac` command, and it needs your own PostgreSQL. To install Core with Web and nodes, use the [installation guide](https://github.com/MiniMax-AI/OpenAgentCore/blob/@SOURCE_REVISION@/docs/getting-started/install.md). - -## Verify and extract - -Verify the archive checksum supplied with the package, then extract it into a new directory under `~/.oac/`. Keep configuration outside the extracted package so that replacing the binaries does not replace credentials or state. - -```sh -sha256sum -c @ARCHIVE_NAME@.tar.gz.sha256 -mkdir -p "$HOME/.oac/releases" -tar -xzf @ARCHIVE_NAME@.tar.gz -C "$HOME/.oac/releases" -cd "$HOME/.oac/releases/@ARCHIVE_NAME@" -sha256sum -c SHA256SUMS -core_bin_dir="$PWD/bin" -``` - -`manifest.json` records the source commit and tree, the platform, the Go version, the pinned upstream protocol and the binary hashes. Checksums detect changed bytes; obtain the archive and its checksum from a trusted source. - -## Configure and start - -Create a dedicated PostgreSQL database and account. Generate a random Core key, keep it in private storage, and write its lowercase hex SHA-256 digest as a JSON array to `core-key-digests.json`: - -```sh -umask 077 -core_config_dir="$HOME/.oac/oac-core-deployment" -mkdir -p "$core_config_dir" -export OAC_DATABASE_URL='postgres://:@/' -export OAC_CORE_KEY_DIGESTS_FILE="$core_config_dir/core-key-digests.json" -export OAC_ADDR=127.0.0.1:8091 -export OAC_PUBLIC_URL=http://127.0.0.1:8091 -``` - -`OAC_DATABASE_URL` and `OAC_CORE_KEY_DIGESTS_FILE` are required. `OAC_PUBLIC_URL` is the origin that applications and machines use to reach Core: an HTTPS origin, or plain HTTP on loopback only. The [configuration reference](https://github.com/MiniMax-AI/OpenAgentCore/blob/@SOURCE_REVISION@/docs/configuration.md#appendix-core-environment-without-the-installer) lists every variable. Run the migrations, then start Core in the foreground or under your own supervisor: - -```sh -"$core_bin_dir/oac-core-migrate" -"$core_bin_dir/oac-core" -``` - -`GET /healthz` reports liveness. One Core process serves each database; replicas add no availability. Keep the database when you replace the binaries. Put a TLS reverse proxy in front for remote clients. - -## Next steps - -- Use the Core key with the [administrator API](https://github.com/MiniMax-AI/OpenAgentCore/blob/@SOURCE_REVISION@/contracts/agents-api/admin-api.md) to create a Project and issue its API key, then follow the [quickstart](https://github.com/MiniMax-AI/OpenAgentCore/blob/@SOURCE_REVISION@/docs/getting-started/quickstart.md). -- To run Sessions on your own machines, follow the [self-hosted guide](https://github.com/MiniMax-AI/OpenAgentCore/blob/@SOURCE_REVISION@/docs/getting-started/self-hosted.md). The one-command installation needs the native installer catalog of the same commit: set `OAC_NATIVE_INSTALLER_DIR` to the `native-installers` directory of that commit's Core distribution, as the [configuration reference](https://github.com/MiniMax-AI/OpenAgentCore/blob/@SOURCE_REVISION@/docs/configuration.md#appendix-core-environment-without-the-installer) describes. The [executor credential contract](https://github.com/MiniMax-AI/OpenAgentCore/blob/@SOURCE_REVISION@/contracts/agents-api/environment-executor-credentials.md) covers issuing credentials with the Core key and `oac-core-environment-key`. -- To add sandbox nodes with `oac-node`, see the [node guide](https://github.com/MiniMax-AI/OpenAgentCore/blob/@SOURCE_REVISION@/docs/getting-started/nodes.md). From b42df79db5f0dfe9539f39652cdabf236aa50b11 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 08:44:54 +0000 Subject: [PATCH 5/9] ci: test the distribution's Core image instead of a standalone one check-core-container ran the read-only official-client suite against an image built from services/core/Dockerfile, which no release ships. Build the Core image context in scripts/build-core-image-context.sh, which the distribution builder now also calls, and have docker-build-core build deploy/distribution/Dockerfile from it. Delete build-core-image.sh, the standalone Dockerfile and the guide for running the standalone container. --- .github/workflows/api-acceptance.yml | 8 +++++--- Makefile | 9 +++++++-- docs/configuration.md | 2 +- docs/maintainers.md | 30 ++-------------------------- scripts/build-core-distribution.sh | 11 ++-------- scripts/build-core-image-context.sh | 20 +++++++++++++++++++ scripts/build-core-image.sh | 24 ---------------------- services/core/Dockerfile | 13 ------------ services/core/README.md | 3 +-- 9 files changed, 38 insertions(+), 82 deletions(-) create mode 100755 scripts/build-core-image-context.sh delete mode 100755 scripts/build-core-image.sh delete mode 100644 services/core/Dockerfile diff --git a/.github/workflows/api-acceptance.yml b/.github/workflows/api-acceptance.yml index cee86b15a..2502be03a 100644 --- a/.github/workflows/api-acceptance.yml +++ b/.github/workflows/api-acceptance.yml @@ -12,7 +12,8 @@ on: - 'go.sum' - 'Makefile' - 'scripts/build-core.sh' - - 'scripts/build-core-image.sh' + - 'scripts/build-core-image-context.sh' + - 'deploy/distribution/Dockerfile' - '.github/workflows/api-acceptance.yml' pull_request: paths: @@ -24,7 +25,8 @@ on: - 'go.sum' - 'Makefile' - 'scripts/build-core.sh' - - 'scripts/build-core-image.sh' + - 'scripts/build-core-image-context.sh' + - 'deploy/distribution/Dockerfile' - '.github/workflows/api-acceptance.yml' permissions: @@ -85,7 +87,7 @@ jobs: run: | python services/core/tests/official_client.py go test ./services/core/internal/store -run '^(TestFunctionStateOfficialClientReadsAndLiveEvents|TestSavedReferenceRetryOfficialClient|TestAgentUpdateOfficialClient|TestAgentDeletionOfficialClient|TestSessionAgentFilterOfficialClient|TestSessionDeletionOfficialClient|TestEnvironmentInitialFailureOfficialClient|TestSelfHostedInitialCreationOfficialClient|TestSelfHostedCancellationOfficialClient|TestSelfHostedFunctionsOfficialClient|TestSelfHostedSteeringOfficialClient)$' -count=1 - - name: Verify standalone container distribution + - name: Verify the distribution's Core image env: OAC_TEST_DATABASE_URL: postgres://agents_api:agents_api_test_only@127.0.0.1:${{ job.services.postgres.ports['5432'] }}/oac_ci_tests?sslmode=disable OAC_DEV_CORE_IMAGE: oac-core:ci diff --git a/Makefile b/Makefile index 929c1b097..00f637e85 100644 --- a/Makefile +++ b/Makefile @@ -73,8 +73,13 @@ check-core: build-core PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s services/core/tests -p 'official_diagnostics_test.py' PYTHONDONTWRITEBYTECODE=1 python3 services/core/deploy/e2b/managed_init_test.py +# The distribution's Core image, without the native installer catalog. docker-build-core: - ./scripts/build-core-image.sh + @set -e; root="$${OAC_DEV_HOME:-$$HOME/.oac}"; \ + mkdir -p "$$root/cache/oac-core-builds"; \ + context=$$(mktemp -d "$$root/cache/oac-core-builds/image.XXXXXX"); trap 'rm -rf "$$context"' EXIT; \ + ./scripts/build-core-image-context.sh "$$context"; \ + docker build --platform linux/amd64 --tag "$${OAC_DEV_CORE_IMAGE:-oac-core:dev}" "$$context" check-core-container: docker-build-core OAC_DEV_CORE_IMAGE="$${OAC_DEV_CORE_IMAGE:-oac-core:dev}" OAC_TEST_SERVER_BIN="$(CURDIR)/services/core/tests/container_server.py" $${OAC_TEST_OFFICIAL_SDK_PYTHON:-python3} services/core/tests/official_client.py @@ -153,7 +158,7 @@ check-distribution: PYTHONDONTWRITEBYTECODE=1 python3 scripts/publish-core-release.test.py PYTHONDONTWRITEBYTECODE=1 python3 scripts/install-release.test.py PYTHONDONTWRITEBYTECODE=1 python3 scripts/config-reference.py --check - bash -n deploy/install/install.sh deploy/install-release.sh scripts/build-web.sh scripts/build-core-distribution.sh scripts/prepare-release-runtimes.sh + bash -n deploy/install/install.sh deploy/install-release.sh scripts/build-web.sh scripts/build-core-distribution.sh scripts/build-core-image-context.sh scripts/prepare-release-runtimes.sh ./scripts/build-web.sh build-core-distribution: diff --git a/docs/configuration.md b/docs/configuration.md index bf3bc003d..b258e0d05 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -134,7 +134,7 @@ In Docker, the Compose project is named `oac-<10 hex digits>` (`project` in `sta ## Appendix: Core environment without the installer -Core reads only its environment. The installer renders `generated/core.env` from `config.json`; if you run Core yourself (see [Maintainers and advanced deployments](maintainers.md)), set these variables. Compose loads the file with `env_file` and systemd with `EnvironmentFile`, so Compose must be 2.26.0 or newer. +Core reads only its environment. The installer renders `generated/core.env` from `config.json`; if you run Core yourself (see the [service guide](../services/core/README.md)), set these variables. Compose loads the file with `env_file` and systemd with `EnvironmentFile`, so Compose must be 2.26.0 or newer. | Variable | Set from | | --- | --- | diff --git a/docs/maintainers.md b/docs/maintainers.md index 2c756747e..47c3c8c88 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -147,7 +147,7 @@ With `draft_release=true` the result is an unpublished `build-` draft | Workflow | Runs on | Covers | | --- | --- | --- | | `core-check` (`check.yml`) | Pushes to `main`, every pull request, releases | All `make check` checks in concurrent partitions, plus a daemon build; see the partitions below | -| `api-acceptance` | Pushes to `main` and pull requests that touch Core, its contracts, clients, shared Go code or build scripts | Standalone commands and migration, the pinned official client over HTTP, and the standalone container | +| `api-acceptance` | Pushes to `main` and pull requests that touch Core, its contracts, clients, shared Go code or build scripts | Standalone commands and migration, the pinned official client over HTTP, and the distribution's Core image | | `native-check` (`native.yml`) | Pull requests that touch native sources, shared dependencies or packaging inputs; manual runs; releases | Daemon, process lifecycle, Harness protocols and the installer bundle on Linux, macOS and Windows; uploads the native installers | | `actionlint` | Changes to workflows | Workflow syntax | | `core-release` | Version tags and manual runs | See [Publish a version](#publish-a-version) | @@ -160,7 +160,7 @@ The full gate starts these partitions concurrently: | --- | --- | | `backend` | Dedicated PostgreSQL guard, sqlc freshness, Runtime/shared Go tests, Linux microsandbox helper, standalone Core build and service/client tests, daemon build | | `tooling` | Harness catalog, name guard, distribution/installer, Claude SDK packaging, optional example including browser acceptance, MiniMax companion scripts | -| `web` | TypeScript checks, doctor and Web/client tests, Web build | +| `web` | TypeScript checks and Web/client tests, Web build | | `web-acceptance` (two shards) | The complete Web Playwright suite, split by test files between two isolated runners | Each check has its own named step. Only the backend job needs a database. Each browser job starts its own fixture and Web server, retaining one Playwright worker per runner so tests never share mutable fixtures across concurrent jobs. Failed Web shards upload their reports and traces for seven days. The final `check` job runs after every partition and succeeds only when all results are `success`; failed, cancelled or skipped jobs cannot produce a green required gate. Releases use this same workflow. Local `make check` still runs every check and the unsharded Web suite; `make check-web-unit` and `make check-web-acceptance` expose its Web parts. `OAC_WEB_TEST_SHARD=1/2` selects a shard for focused CI validation. @@ -176,29 +176,3 @@ gh variable set OAC_USE_GITHUB_RUNNERS --body true --repo MiniMax-AI/OpenAgentCo ``` This is an explicit operator switch, not an automatic billing balance probe. Runner selection applies to newly scheduled runs. Check current allowance and platform conversion rates in [Blacksmith's runner documentation](https://docs.blacksmith.sh/blacksmith-runners/overview) before treating 2-vCPU usage as free; Windows minutes consume more allowance than Linux minutes. Standard GitHub runner usage follows the repository's visibility and GitHub plan. These workflows request no Blacksmith runner larger than 2 vCPU and no paid cache add-on. -## Run Core without the installer - -The standalone container gives you Core alone: no Web, no `oac` command and no `config.json`. It suits development, testing and operators who supervise Core themselves. Core reads only its environment; the [configuration appendix](configuration.md#appendix-core-environment-without-the-installer) lists the variables. `OAC_DATABASE_URL` and `OAC_CORE_KEY_DIGESTS_FILE` are required; set `OAC_PUBLIC_URL` to the origin machines use to reach Core, or Core runs without the daemon transport. - -- The [service guide](../services/core/README.md) covers building and running Core from source. - -To run the container, create a private directory (mode 0700) with `api.env` (`OAC_DATABASE_URL` for a dedicated database, reachable from the container, `OAC_CORE_KEY_DIGESTS_FILE=/run/core-key-digests.json`, and `OAC_PUBLIC_URL`) and `core-key-digests.json`, a JSON array with the lowercase hex SHA-256 digest of your Core key. Keep both files mode 0600 and the Core key itself elsewhere. Run the migrations, then start Core: - -```sh -config_dir="$HOME/.oac/oac-core-deployment" -docker run --rm --read-only --cap-drop=ALL --security-opt=no-new-privileges \ - --env-file "$config_dir/api.env" \ - oac-core:dev /usr/local/bin/oac-core-migrate -docker run --name oac-core --detach --read-only \ - --cap-drop=ALL --security-opt=no-new-privileges \ - --user "$(id -u):$(id -g)" \ - --publish 127.0.0.1:8091:8091 \ - --env-file "$config_dir/api.env" \ - --mount "type=bind,source=$config_dir/core-key-digests.json,target=/run/core-key-digests.json,readonly" \ - oac-core:dev -curl --fail http://127.0.0.1:8091/healthz -``` - -`--user` lets the container read the key digest file as your non-root host user; alternatively grant UID 65532 read access and omit it. Put a TLS reverse proxy in front for remote clients. `/healthz` reports liveness only. Keep credentials out of the image. All state is in PostgreSQL, so the container needs no writable volume; stop and start it with `docker stop` and `docker start`, and never remove the database to replace it. One Core process serves each database; replicas add no availability. After startup, use the Core key with the [administrator API](../contracts/agents-api/admin-api.md) to create Projects and issue application keys. - -The image also contains `oac-core-device` for an [internal execution device](../services/core/README.md#internal-execution-device-connection) and `oac-core-environment-key`, the [break-glass credential command](../contracts/agents-api/environment-executor-credentials.md#break-glass-command). diff --git a/scripts/build-core-distribution.sh b/scripts/build-core-distribution.sh index 959a2b3b0..4af28f2f2 100755 --- a/scripts/build-core-distribution.sh +++ b/scripts/build-core-distribution.sh @@ -112,7 +112,7 @@ mkdir -p "$bundle/runtime" cp services/core/deploy/codex/seccomp.json "$bundle/runtime/" cp LICENSE "$bundle/" -OAC_DEV_BUILD_REVISION="$revision" OAC_DEV_CORE_BUILD_DIR="$stage/core/bin" scripts/build-core.sh +OAC_DEV_BUILD_REVISION="$revision" E2B_SOURCE_REVISION="$revision" scripts/build-core-image-context.sh "$stage/core" ( cd services/core/tools/microsandbox-provider GOWORK=off CGO_ENABLED=1 go build -mod=readonly -trimpath \ @@ -130,18 +130,11 @@ else python3 scripts/core-distribution-manifest.py extract-runtime "$msb_archive" "$stage/core/microsandbox" fi mkdir -p "$bundle/native" -cp -R "$stage/core/bin" "$stage/core/microsandbox" "$bundle/native/" -E2B_SOURCE_REVISION="$revision" E2B_PROVIDER_BUILD_DIR="$stage/e2b-build" scripts/build-e2b-provider.sh -mkdir -p "$stage/core/e2b" -tar -xzf "$stage/e2b-build/oac-e2b-provider-linux-amd64.tar.gz" \ - --strip-components=1 -C "$stage/core/e2b" -cp -R "$stage/core/e2b" "$bundle/native/e2b" -mkdir -p "$stage/core/native-installers" +cp -R "$stage/core/bin" "$stage/core/microsandbox" "$stage/core/e2b" "$bundle/native/" if [[ -n "${OAC_NATIVE_INSTALLER_BUILD_DIR:-}" ]]; then python3 scripts/core-distribution-manifest.py native-catalog "$bundle" "$stage" "$revision" \ "$OAC_NATIVE_INSTALLER_BUILD_DIR" "$release_base_url" fi -cp deploy/distribution/Dockerfile "$stage/core/Dockerfile" build_image core "$stage/core" core_image="$(cat "$stage/core.id")" # Fail at packaging time if the helper or runtime requires unavailable host libraries. diff --git a/scripts/build-core-image-context.sh b/scripts/build-core-image-context.sh new file mode 100755 index 000000000..76d73eb32 --- /dev/null +++ b/scripts/build-core-image-context.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Assemble the build context of the distribution's Core image in CONTEXT_DIR: +# the Core commands in bin/, the E2B helper in e2b/, an empty native-installers/ +# and deploy/distribution/Dockerfile. build-core-distribution.sh adds the native +# installer catalog before it builds the image. +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +context="${1:?Usage: build-core-image-context.sh CONTEXT_DIR}" +if [[ "$context" != /* ]]; then + printf 'The Core image context directory must be absolute: %s\n' "$context" >&2 + exit 1 +fi + +mkdir -p "$context/bin" "$context/e2b" "$context/native-installers" +GOOS=linux GOARCH=amd64 OAC_DEV_CORE_BUILD_DIR="$context/bin" "$repo_root/scripts/build-core.sh" +E2B_PROVIDER_BUILD_DIR="$context/e2b-build" "$repo_root/scripts/build-e2b-provider.sh" +tar -xzf "$context/e2b-build/oac-e2b-provider-linux-amd64.tar.gz" --strip-components=1 -C "$context/e2b" +rm -rf "$context/e2b-build" +cp "$repo_root/deploy/distribution/Dockerfile" "$context/Dockerfile" diff --git a/scripts/build-core-image.sh b/scripts/build-core-image.sh deleted file mode 100755 index e1135eb03..000000000 --- a/scripts/build-core-image.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -runtime_root="${OAC_DEV_HOME:-$HOME/.oac}" -image="${OAC_DEV_CORE_IMAGE:-oac-core:dev}" -if [[ "$runtime_root" != /* ]]; then - printf 'OAC_DEV_HOME must be absolute: %s\n' "$runtime_root" >&2 - exit 1 -fi -mkdir -p "$runtime_root/cache/oac-core-builds" -image_context="$(mktemp -d "$runtime_root/cache/oac-core-builds/image.XXXXXX")" -trap 'rm -rf "$image_context"' EXIT - -# Reuse the source boundary; never send the repository or runtime keys to Docker. -GOOS=linux GOARCH=amd64 OAC_DEV_CORE_BUILD_DIR="$image_context" \ - "$repo_root/scripts/build-core.sh" -E2B_PROVIDER_BUILD_DIR="$image_context/e2b-build" "$repo_root/scripts/build-e2b-provider.sh" -mkdir -p "$image_context/e2b" -tar -xzf "$image_context/e2b-build/oac-e2b-provider-linux-amd64.tar.gz" \ - --strip-components=1 -C "$image_context/e2b" -rm -rf "$image_context/e2b-build" -cp "$repo_root/services/core/Dockerfile" "$image_context/Dockerfile" -docker build --platform linux/amd64 --tag "$image" "$image_context" diff --git a/services/core/Dockerfile b/services/core/Dockerfile deleted file mode 100644 index 3a6607367..000000000 --- a/services/core/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -# Linux amd64 runtime. Build with make docker-build-core. -# The context contains independently built executables and the pinned cloud SDK helper. -FROM debian:bookworm-slim@sha256:3783cc01769c7b2b1b83a5c5ad96c815348e28ed7da68e2e3687004faa906251 -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates libgcc-s1 \ - && rm -rf /var/lib/apt/lists/* - -COPY --chmod=0555 oac-core oac-core-migrate oac-core-device oac-core-environment-key oac-node /usr/local/bin/ -COPY e2b/ /opt/oac/e2b/ -ENV OAC_ADDR=:8091 -ENV OAC_E2B_PROVIDER_BIN=/opt/oac/e2b/oac-e2b-provider -EXPOSE 8091 -USER 65532:65532 -CMD ["/usr/local/bin/oac-core"] diff --git a/services/core/README.md b/services/core/README.md index 2e33d2bcb..0c1eea029 100644 --- a/services/core/README.md +++ b/services/core/README.md @@ -77,8 +77,7 @@ Node, Docker, the product service or frontend. An isolated source context enforc that boundary on every build. [Contributor rules](../../docs/maintainers.md#standalone-core-builds) define the allowed shared packages and required checks. Runtime database/key configuration and a separately installed execution daemon are still required; -these binaries do not establish full protocol coverage. For a standalone Linux -container, see [Run Core without the installer](../../docs/maintainers.md#run-core-without-the-installer). +these binaries do not establish full protocol coverage. ## Database ownership From 3fe09e2de9bfe4e8e3cdaae98beffd3cb05664b1 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 08:45:28 +0000 Subject: [PATCH 6/9] chore: delete the manual Subagent acceptance script Nothing ran scripts/core-subagents-acceptance.py; Go store and API tests cover the Subagent read rows. Drop the sentences in the Subagent contract that described the script. --- scripts/core-subagents-acceptance.py | 549 --------------------------- 1 file changed, 549 deletions(-) delete mode 100644 scripts/core-subagents-acceptance.py diff --git a/scripts/core-subagents-acceptance.py b/scripts/core-subagents-acceptance.py deleted file mode 100644 index 6689034e7..000000000 --- a/scripts/core-subagents-acceptance.py +++ /dev/null @@ -1,549 +0,0 @@ -#!/usr/bin/env python3 -"""Real-model acceptance of the six pinned Subagent GET operations. - -Install openai 3.13.0 from the commit in contracts/agents-api/upstream.json. -Required ENV: OAC_TEST_BASE_URL (including /v1), OAC_TEST_TOKEN, and -OAC_TEST_FOREIGN_TOKEN (a valid key for a different project). Creating a Session -also requires OAC_TEST_MODEL. OAC_TEST_ENVIRONMENT_JSON defaults to -{"type":"none"}; OAC_TEST_HARNESS optionally selects the existing Core extension. -OAC_TEST_SESSION_ID selects an existing Session. Close/resume use the native -conversation context from spawn; nullable names/instructions are not prerequisites. - -Run --phase all for the nested/close/resume native profile, --phase spawn-direct -for common reads with two real children, or spawn, inspect, close, resume using -the same --evidence directory under ~/.oac. Inspect never submits model input. -For an independently prepared Session, inspect requires two real children and -proves reads only, not the full lifecycle. Use --require-nested when the native -profile supports nested delegation; absence is recorded, not fabricated. All/full acceptance requires observed spawn, nested, close and resume -facts; model prose is never accepted as evidence of a native action. - -Child work must appear only where the official service shows it. Inspection -records each of these visibility checks under "visibility" (per phase) and fails -only after running all of them, so a baseline run reports every difference at once: -Session Turn list/retrieve/cursor carry root Turns only and a child Turn ID -answers like a missing one (also for the foreign project); child Turns carry the -Session's Agent ID with their own subagent_id; every list uses the object/first_id/ -last_id envelope with null IDs on an empty page; child Item lists clamp limit 0 -and 101 while the Subagent and Subagent Turn lists reject them; and the Session -stream observed while this script submitted input (the creation stream, or the -GET stream for later input) carries no child Turn or Item event, and carries -agent.session.subagent.created when children were spawned. - -Evidence contains only IDs, timestamps, counts and named checks. No HTTP bodies, -model output, API keys or provider configuration are written. Sessions are left -for the operator to inspect and clean up. This script starts no service/runtime. -""" - -import argparse -import importlib.metadata -import json -import os -from pathlib import Path -import re -import sys -import threading -import time -from urllib.parse import quote, urlsplit -import uuid - - -class AcceptanceFailure(Exception): - pass - - -class StreamObserver: - """Summarizes Session events without retaining bodies. The reader is a daemon - thread; stop() reads what was observed even if the live stream stays open.""" - - def __init__(self, stream, kind): - self.stream, self.kind = stream, kind - self.events, self.session_id, self.error, self.ended, self.stopping = [], None, None, False, False - self.thread = threading.Thread(target=self.run, daemon=True) - self.thread.start() - - def run(self): - try: - for event in self.stream: - value = event.to_dict() - if value.get("type") == "agent.session.created": - self.session_id = (value.get("session") or {}).get("id") - turn, item = value.get("turn") or {}, value.get("item") or {} - self.events.append({"type": value.get("type"), "turn_ids": {v for v in ( - value.get("turn_id"), turn.get("id"), item.get("turn_id")) if v}, - "child_turn": turn.get("subagent_id") is not None}) - except Exception as error: - if not self.stopping: - self.error = type(error).__name__ - finally: - self.ended = True - - def stop(self): - if self.kind == "creation": - # The creation stream ends by itself once the Session settles. - self.thread.join(60) - if not self.ended: - # A GET stream stays open; closing it ends the reader with a connection error. - self.stopping = True - self.stream.close() - self.thread.join(5) - return list(self.events) - - -def require(condition, code): - if not condition: - raise AcceptanceFailure(code) - - -def identifier(value): - require(isinstance(value, str) and re.fullmatch(r"[A-Za-z0-9_-]{1,200}", value), "invalid_resource_id") - return value - - -def main(): - parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) - parser.add_argument("--phase", choices=("spawn", "spawn-direct", "inspect", "close", "resume", "all"), default="inspect") - parser.add_argument("--evidence", required=True, type=Path) - parser.add_argument("--timeout", type=int, default=240) - parser.add_argument("--require-nested", action="store_true", help="Require native nested delegation in an externally prepared fixture") - args = parser.parse_args() - require(1 <= args.timeout <= 1800, "invalid_timeout") - evidence = args.evidence.expanduser().resolve() - require(Path.home().joinpath(".oac") in evidence.parents, "evidence_must_be_under_oac_home") - evidence.mkdir(parents=True, exist_ok=True, mode=0o700) - path = evidence / "subagents-proof.json" - report = json.loads(path.read_text()) if path.exists() else { - "passed": False, "checks": [], "phases": {}, "nonce": uuid.uuid4().hex, - } - token = os.environ.get("OAC_TEST_TOKEN", "") - foreign_token = os.environ.get("OAC_TEST_FOREIGN_TOKEN", "") - - def save(): - raw = json.dumps(report, indent=2) + "\n" - require(not any(secret and secret in raw for secret in (token, foreign_token)), "secret_in_evidence") - temporary = path.with_suffix(".tmp") - fd = os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) - with os.fdopen(fd, "w") as stream: - stream.write(raw) - os.replace(temporary, path) - - def checked(name): - if name not in report["checks"]: - report["checks"].append(name) - - client = foreign = http = None - try: - pin = json.loads((Path(__file__).resolve().parents[1] / "contracts/agents-api/upstream.json").read_text()) - distribution = importlib.metadata.distribution("openai") - source = json.loads(distribution.read_text("direct_url.json") or "{}") - require(distribution.version == pin["sdk_version"] == "3.13.0" and - source.get("vcs_info", {}).get("commit_id") == pin["commit"], "pinned_sdk_required") - report.update(sdk_version=pin["sdk_version"], sdk_commit=pin["commit"], passed=False) - base = os.environ.get("OAC_TEST_BASE_URL", "").rstrip("/") - parsed = urlsplit(base) - require(parsed.scheme in ("http", "https") and parsed.netloc and parsed.path.endswith("/v1") - and not parsed.username and not parsed.password and not parsed.query and not parsed.fragment, - "invalid_base_url") - require(token and foreign_token and token != foreign_token, "distinct_project_tokens_required") - import httpx2 - from openai import NotFoundError, OpenAI - client = OpenAI(base_url=base, api_key=token, max_retries=0, timeout=30, - _strict_response_validation=True, http_client=httpx2.Client(trust_env=False)) - foreign = OpenAI(base_url=base, api_key=foreign_token, max_retries=0, timeout=30, - _strict_response_validation=True, http_client=httpx2.Client(trust_env=False)) - http = httpx2.Client(trust_env=False, timeout=30) - # A bad credential cannot accidentally count as tenant-isolation proof. - foreign.beta.agents.sessions.list(limit=1) - sessions = client.beta.agents.sessions - supplied = os.environ.get("OAC_TEST_SESSION_ID") - if supplied: - require(not report.get("session_id") or report["session_id"] == supplied, "evidence_session_mismatch") - report["session_id"] = identifier(supplied) - headers = {"Authorization": "Bearer " + token, "OpenAI-Beta": "agents=v1"} - other_headers = {**headers, "Authorization": "Bearer " + foreign_token} - - def sid(): - return identifier(report["session_id"]) - - def endpoint(suffix=""): - return base + "/agents/sessions/" + quote(sid(), safe="") + suffix - - def raw(suffix, params=None, expected=200, other=False): - response = http.get(endpoint(suffix), headers=other_headers if other else headers, params=params) - require(response.status_code == expected, "raw_status_" + str(expected) + "_expected") - body = response.json() - if expected != 200: - require(isinstance(body.get("error"), dict), "error_envelope_missing") - return body - - def cursor_error(suffix, after): - """An unresolved cursor is the family's official 400 (ERROR-PROTOCOL-001).""" - message = "Invalid session item ID in `after`" if suffix.endswith("/items") else "Invalid resource ID in `after`" - error = raw(suffix, {"after": after}, expected=400)["error"] - require(error == {"message": message, "type": "invalid_request_error", "code": "invalid_request_error", - "param": None}, "cursor_error_mismatch") - - def sdk_list(resource, *pos, **keywords): - values, seen = [], set() - for item in resource.list(*pos, limit=100, order="asc", **keywords): - require(item.id not in seen, "sdk_cursor_repeated_resource") - seen.add(item.id) - values.append(item.to_dict()) - require(len(values) <= 5000, "history_bound_exceeded") - return values - - def all_turns(): - return sdk_list(sessions.turns, sid()) - - def children(): - return sdk_list(sessions.subagents, sid()) - - def wait_for(predicate, code, timeout=None): - deadline = time.monotonic() + (args.timeout if timeout is None else timeout) - while time.monotonic() < deadline: - value = predicate() - if value: - return value - time.sleep(1) - raise AcceptanceFailure(code) - - def root_finished(before): - turns = [turn for turn in all_turns() if turn.get("subagent_id") is None and turn["id"] not in before] - require(len(turns) <= 1, "concurrent_root_input_detected") - if not turns: - return False - turn = turns[0] - require(turn["status"] not in ("failed", "cancelled"), "root_execution_failed") - return turn if turn["status"] == "completed" else False - - observers = {} - - def submit(stage, prompt): - require(report["phases"].get(stage) != "passed", "phase_already_passed") - require(not report.get("pending_phase"), "pending_phase_requires_operator_reconciliation") - before = {turn["id"] for turn in all_turns() if turn.get("subagent_id") is None} if report.get("session_id") else set() - report["pending_phase"] = stage - save() - if not report.get("session_id"): - model = os.environ.get("OAC_TEST_MODEL") - require(model, "model_required_for_creation") - agent = {"model": model, "multi_agent": {"enabled": True, "max_concurrent_subagents": 4}} - harness = os.environ.get("OAC_TEST_HARNESS") - if harness: - agent["x_agents_core"] = {"harness": harness} - environment = json.loads(os.environ.get("OAC_TEST_ENVIRONMENT_JSON", '{"type":"none"}')) - # The creation stream is the Session stream observed for this phase. - observer = StreamObserver(sessions.create(agent=agent, environment=environment, input=prompt, stream=True, - extra_headers={"Idempotency-Key": report["nonce"] + "-" + stage}), "creation") - observers[stage] = observer - wait_for(lambda: observer.session_id or observer.ended, "creation_stream_timeout", timeout=30) - require(observer.session_id, "creation_stream_missing_created_event") - report["session_id"] = identifier(observer.session_id) - save() - else: - require(sessions.retrieve(sid()).agent.multi_agent.enabled, "existing_session_delegation_disabled") - # Open the live stream before submitting, as the stream is live-only. - observers[stage] = StreamObserver(sessions.events.stream(sid()), "events") - sessions.events.create(sid(), events=[{"type": "agent.session.input.message", "input": [ - {"role": "user", "content": [{"type": "input_text", "text": prompt}]}]}], - idempotency_key=report["nonce"] + "-" + stage) - turn = wait_for(lambda: root_finished(before), "root_turn_timeout") - report.setdefault("root_turns", {})[stage] = identifier(turn["id"]) - report.pop("pending_phase", None) - save() - - def page_check(suffix, expected): - """Pagination and scope checks that predate the visibility batch.""" - expected_ids = [identifier(item["id"]) for item in expected] - require(len(expected_ids) == len(set(expected_ids)), "duplicate_resource_id") - for order in ("asc", "desc"): - for limit in (1, 2): - observed, after = [], None - while True: - params = {"order": order, "limit": limit} - if after: - params["after"] = after - page = raw(suffix, params) - require(isinstance(page.get("data"), list) and isinstance(page.get("has_more"), bool), "invalid_page_shape") - require(len(page["data"]) <= limit, "page_limit_not_enforced") - observed.extend(page["data"]) - require(len(observed) <= len(expected), "pagination_duplicate_or_unstable_history") - if not page["has_more"]: - break - require(page["data"] and page["data"][-1]["id"] != after, "cursor_did_not_advance") - after = page["data"][-1]["id"] - require(observed == (expected if order == "asc" else list(reversed(expected))), "sdk_raw_or_pagination_mismatch") - defaults = raw(suffix) - require(defaults["data"] == list(reversed(expected))[:20], "pagination_defaults_mismatch") - # Unknown list keys are ignored (list query tolerance, row A1). - require(raw(suffix, {"unknown": "value"})["data"] == defaults["data"], "unknown_list_key_not_ignored") - cursor_error(suffix, str(uuid.uuid4())) - raw(suffix, {"order": "invalid"}, expected=400) - raw(suffix, other=True, expected=404) - - visibility = {} - - def visible(name, check): - """Runs one visibility check to completion and records its result.""" - try: - check() - result = "passed" - except AcceptanceFailure as error: - result = str(error) - # A check repeated per resource keeps its first failure. - if visibility.get(name, "passed") == "passed": - visibility[name] = result - - def envelope_check(suffix, expected, clamped): - ids = [item["id"] for item in expected] - window = ids[:100] - first = raw(suffix, {"order": "asc", "limit": 100}) - require(first.get("object") == "list" and first.get("first_id") == (window[0] if window else None) and - first.get("last_id") == (window[-1] if window else None), "list_envelope_mismatch") - if ids: - empty = raw(suffix, {"order": "asc", "after": ids[-1]}) - require(empty == {"object": "list", "data": [], "first_id": None, "last_id": None, "has_more": False}, - "empty_page_envelope_mismatch") - for limit, size in ((0, 1), (101, 100)): - if clamped: - page = raw(suffix, {"order": "asc", "limit": limit}) - require([item["id"] for item in page["data"]] == ids[:size] and page["has_more"] == (len(ids) > size), - "limit_" + str(limit) + "_not_clamped") - else: - raw(suffix, {"limit": limit}, expected=400) - - def inspect(stage=None): - visibility.clear() - subs = children() - require(subs, "fixture_not_observed_no_subagents") - page_check("/subagents", subs) - visible("subagent_list_envelope_and_limit_rejection", lambda: envelope_check("/subagents", subs, False)) - root_items = sdk_list(sessions.items, sid()) - turns = all_turns() - require(all("subagent_id" in turn for turn in turns), "turn_subagent_identity_field_missing") - by_turn = {turn["id"]: turn for turn in turns} - root_ids = {item["id"] for item in root_items} - require(all(item["turn_id"] in by_turn for item in root_items), "root_items_include_child_work") - known = {sub["id"] for sub in subs} - root = sessions.retrieve(sid()).agent.id - child_turn_ids = set() - require(all(sub["session_id"] == sid() and sub["parent_agent_id"] in known | {root} for sub in subs), "subagent_parent_scope_mismatch") - parents = {sub["id"]: sub["parent_agent_id"] for sub in subs} - for child in parents: - lineage = set() - while child != root: - require(child not in lineage and child in parents, "invalid_subagent_parent_graph") - lineage.add(child) - child = parents[child] - nested = [sub["id"] for sub in subs if sub["parent_agent_id"] in known] - if args.require_nested or args.phase in ("spawn", "all"): - require(nested, "fixture_not_observed_no_nested_child") - require(len(subs) >= 2, "fixture_not_observed_two_children_for_scope_checks") - seen_items, summaries = set(root_ids), [] - for sub in subs: - child = identifier(sub["id"]) - suffix = "/subagents/" + child - fetched = sessions.subagents.retrieve(child, session_id=sid()).to_dict() - require(fetched == sub == raw(suffix), "subagent_retrieve_mismatch") - raw(suffix, other=True, expected=404) - child_turns = sdk_list(sessions.subagents.turns, child, session_id=sid()) - child_items = sdk_list(sessions.subagents.items, child, session_id=sid()) - require(child_turns and child_items, "fixture_not_observed_missing_child_history") - require(all(turn["status"] in ("completed", "failed", "cancelled") for turn in child_turns), "child_history_not_settled") - require(any(turn["status"] == "completed" for turn in child_turns), "fixture_not_observed_child_completion") - require(any(item.get("type") == "message" and item.get("role") == "assistant" and item.get("content") - for item in child_items), "fixture_not_observed_child_model_output") - page_check(suffix + "/turns", child_turns) - page_check(suffix + "/items", child_items) - visible("child_turn_list_envelope_and_limit_rejection", lambda: envelope_check(suffix + "/turns", child_turns, False)) - visible("child_item_list_envelope_and_limit_clamp", lambda: envelope_check(suffix + "/items", child_items, True)) - own_turn_ids = {turn["id"] for turn in child_turns} - own_item_ids = {item["id"] for item in child_items} - require(not own_item_ids & seen_items and all(item["turn_id"] in own_turn_ids for item in child_items), "items_cross_agent_boundary") - seen_items |= own_item_ids - collected = set() - for turn in child_turns: - tid = identifier(turn["id"]) - child_turn_ids.add(tid) - require(turn["subagent_id"] == child and turn["session_id"] == sid(), "child_turn_owner_mismatch") - # The official child Turn keeps the Session's Agent ID (SAT-08). - visible("child_turn_session_agent_id", lambda: require(turn["agent_id"] == root, "child_turn_agent_id_not_session_agent")) - fetched = sessions.subagents.turns.retrieve(tid, session_id=sid(), subagent_id=child).to_dict() - require(fetched == turn, "subagent_turn_retrieve_mismatch") - require(raw(suffix + "/turns/" + tid) == turn, "raw_child_turn_mismatch") - raw(suffix + "/turns/" + tid, other=True, expected=404) - visible("session_turn_routes_hide_child_turns", lambda: session_child_turn_check(tid)) - items = sdk_list(sessions.subagents.turns.items, tid, session_id=sid(), subagent_id=child) - page_check(suffix + "/turns/" + tid + "/items", items) - visible("child_turn_item_list_envelope_and_limit_clamp", lambda: envelope_check(suffix + "/turns/" + tid + "/items", items, True)) - require(all(item["turn_id"] == tid for item in items), "turn_items_wrong_turn") - require(items == [item for item in child_items if item["turn_id"] == tid], "per_turn_items_mismatch") - collected.update(item["id"] for item in items) - require(collected == own_item_ids, "child_item_history_incomplete") - # A real root-owned cursor/Turn must not become valid in the child scope. - root_turn = next((turn["id"] for turn in turns if turn.get("subagent_id") is None), None) - require(root_turn, "fixture_not_observed_root_turn") - raw(suffix + "/turns/" + root_turn, expected=404) - raw(suffix + "/turns/" + root_turn + "/items", expected=404) - cursor_error(suffix + "/turns", root_turn) - if root_items: - cursor_error(suffix + "/items", root_items[0]["id"]) - summaries.append({"id": child, "parent_agent_id": identifier(sub["parent_agent_id"]), - "status": sub["status"], "opened_at": sub["opened_at"], "closed_at": sub["closed_at"], - "turn_ids": sorted(own_turn_ids), "item_ids": sorted(own_item_ids)}) - first, second = summaries[:2] - raw("/subagents/" + first["id"] + "/turns/" + second["turn_ids"][0], expected=404) - cursor_error("/subagents/" + first["id"] + "/items", second["item_ids"][0]) - # Session Turn reads carry root work only (SAT-07). - visible("session_turns_root_only", lambda: require( - all(turn.get("subagent_id") is None and turn["agent_id"] == root for turn in turns) and - not child_turn_ids & set(by_turn), "session_turns_include_child_work")) - visible("session_turn_list_envelope_and_limit_rejection", lambda: envelope_check("/turns", turns, False)) - if stage in observers: - visible("session_stream_root_work_only", lambda: stream_check(stage, set(by_turn), child_turn_ids)) - report.update(subagents=summaries, nested_ids=nested, root_item_count=len(root_items)) - report.setdefault("visibility", {})[stage or "inspect"] = dict(sorted(visibility.items())) - for name in ("six_get_sdk_and_raw", "root_child_item_isolation", "pagination_asc_desc_after_limit", - "invalid_and_wrong_scope_cursors", "foreign_project_scope"): - checked(name) - if nested: - checked("nested_parentage") - failed = sorted(name for name, result in visibility.items() if result != "passed") - require(not failed, "visibility_failed_" + "_".join(failed)) - for name in visibility: - checked(name) - report["resources_passed"] = True - report["phases"]["inspect"] = "passed" - save() - - def session_child_turn_check(tid): - # A child Turn ID answers exactly like a missing Turn, as a path and as a cursor. - missing = str(uuid.uuid4()) - require(raw("/turns/" + tid, expected=404) == raw("/turns/" + missing, expected=404), "child_turn_retrieve_differs_from_missing") - require(raw("/turns", {"after": tid}, expected=404) == raw("/turns", {"after": missing}, expected=404), "child_turn_cursor_differs_from_missing") - raw("/turns/" + tid, other=True, expected=404) - try: - sessions.turns.retrieve(tid, session_id=sid()) - except NotFoundError: - return - raise AcceptanceFailure("sdk_session_turn_retrieve_returned_child_turn") - - def stream_check(stage, root_turn_ids, child_turn_ids): - observer = observers[stage] - events = observer.stop() - counts = {} - for event in events: - counts[event["type"]] = counts.get(event["type"], 0) + 1 - report.setdefault("streams", {})[stage] = {"kind": observer.kind, "error": observer.error, "event_counts": dict(sorted(counts.items()))} - require(observer.error is None, "session_stream_failed") - referenced = set().union(*(event["turn_ids"] for event in events)) if events else set() - require(not referenced & child_turn_ids and not any(event["child_turn"] for event in events), "session_stream_carries_child_work") - require(referenced <= root_turn_ids, "session_stream_references_unknown_turn") - if stage in ("spawn", "spawn-direct"): - require(counts.get("agent.session.subagent.created", 0) >= 1, "fixture_not_observed_subagent_created_event") - - marker = "subagent-proof-" + report["nonce"][:12] - - def spawn(): - submit("spawn", "Use native subagent tools for this acceptance task; do not simulate delegation. " - f"Create two direct children. Give the first child the exact task marker {marker}-alpha " - f"in its task instructions. Ask it to create a nested child with marker {marker}-nested, " - "ask that nested child to compute 13 + 29, wait for its answer, then report the result. " - f"Give the second direct child task marker {marker}-beta and ask it to compute 7 * 8. " - "Wait for both direct children and report their results. Keep every child open and available; " - "do not close or interrupt them. Use no network or file tools.") - wait_for(lambda: len(children()) >= 3, "fixture_not_observed_spawn_and_nested", timeout=5) - root = sessions.retrieve(sid()).agent.id - direct = [sub for sub in children() if sub["parent_agent_id"] == root] - require(len(direct) >= 2, "fixture_not_observed_two_direct_children") - require(all(sub["status"] == "active" and sub["closed_at"] is None for sub in direct), "spawned_child_not_active") - inspect("spawn") - report["phases"]["spawn"] = "passed" - save() - - def spawn_direct(): - submit("spawn-direct", "Use your native subagent task tools; do not simulate delegation. " - f"Create two direct child sessions. Give the first marker {marker}-alpha " - "and ask it to compute 13 + 29. Give the second marker " - f"{marker}-beta and ask it to compute 7 * 8. " - "Wait for both native child tasks and report their results. " - "Do not create nested children, close or interrupt either child. " - "Use no network or file tools.") - wait_for(lambda: len(children()) >= 2, "fixture_not_observed_two_children", timeout=5) - inspect("spawn-direct") - report["phases"]["spawn-direct"] = "passed" - save() - - def close_child(): - require(report["phases"].get("spawn") == "passed", "spawn_evidence_required") - root = sessions.retrieve(sid()).agent.id - before = {sub["id"]: sub for sub in children() if sub["parent_agent_id"] == root} - require(before and all(sub["status"] == "active" for sub in before.values()), "close_requires_active_children") - histories = {child: { - "turns": [turn["id"] for turn in sdk_list(sessions.subagents.turns, child, session_id=sid())], - "items": [item["id"] for item in sdk_list(sessions.subagents.items, child, session_id=sid())], - } for child in before} - submit("close", f"Use the native close tool to close only your existing direct child whose original task marker is {marker}-alpha. " - "Native processes may have been released between these root Turns. First use native resume on that SAME child ID " - "to load it if necessary, then invoke native close. Do not send a new task, create a replacement or merely interrupt it. " - "Leave the beta child open. Check the actual native close result and report failure if it failed.") - changed = wait_for(lambda: [sub for sub in children() if sub["id"] in before and sub["status"] == "closed"], - "fixture_not_observed_closed_child", timeout=5) - require(len(changed) == 1, "fixture_not_observed_single_closed_child") - closed = changed[0] - child = identifier(closed["id"]) - require(closed["opened_at"] == before[child]["opened_at"] and isinstance(closed["closed_at"], int), "closed_lifecycle_mismatch") - report.update(target_id=child, target_opened_at=closed["opened_at"], target_closed_at=closed["closed_at"], - before_close_turns=histories[child]["turns"], before_close_items=histories[child]["items"]) - inspect("close") - report["phases"]["close"] = "passed" - checked("close_preserves_identity_and_opened_at") - save() - - def resume_child(): - require(report["phases"].get("close") == "passed", "close_evidence_required") - require(sessions.subagents.retrieve(report["target_id"], session_id=sid()).status == "closed", "resume_requires_closed_child") - submit("resume", f"Use the native resume tool on the SAME closed child with original task marker {marker}-alpha. " - "Do not create a replacement. Send it a new task to compute 19 + 23, wait for its reply, " - "and leave it open and available. Report only after the native task finishes.") - resumed = wait_for(lambda: next((sub for sub in children() if sub["id"] == report["target_id"] and sub["status"] == "active"), None), - "fixture_not_observed_resumed_child", timeout=5) - require(resumed["opened_at"] == report["target_opened_at"] and resumed["closed_at"] is None, "resume_lifecycle_mismatch") - own_turns = sdk_list(sessions.subagents.turns, resumed["id"], session_id=sid()) - own_items = sdk_list(sessions.subagents.items, resumed["id"], session_id=sid()) - require(set(report["before_close_turns"]) < {turn["id"] for turn in own_turns}, "fixture_not_observed_new_resumed_turn") - require(any(turn["id"] not in report["before_close_turns"] and turn["status"] == "completed" for turn in own_turns), - "fixture_not_observed_resumed_turn_completion") - require(set(report["before_close_items"]) <= {item["id"] for item in own_items}, "resume_lost_item_history") - inspect("resume") - report["phases"]["resume"] = "passed" - checked("resume_same_id_opened_at_null_closed_at_and_retained_history") - save() - - phases = {"spawn": spawn, "spawn-direct": spawn_direct, "inspect": inspect, "close": close_child, "resume": resume_child} - for phase in ("spawn", "close", "resume") if args.phase == "all" else (args.phase,): - phases[phase]() - report["passed"] = all(report["phases"].get(phase) == "passed" for phase in ("spawn", "inspect", "close", "resume")) - report["requested_phase_passed"] = True - report.pop("failure", None) - report.pop("http_status", None) - except Exception as error: - report.update(passed=False, requested_phase_passed=False, - failure=str(error) if isinstance(error, AcceptanceFailure) else type(error).__name__) - if isinstance(getattr(error, "status_code", None), int): - report["http_status"] = error.status_code - raise - finally: - for connection in (client, foreign, http): - if connection is not None: - connection.close() - save() - print(json.dumps({key: report.get(key) for key in ("passed", "resources_passed", "requested_phase_passed", "session_id", "target_id", "phases", "failure")})) - - -if __name__ == "__main__": - try: - main() - except Exception as error: - # API exception messages may contain response bodies or expanded credentials. - print(json.dumps({"error": str(error) if isinstance(error, AcceptanceFailure) else type(error).__name__}), file=sys.stderr) - sys.exit(1) From ce8ad6bf3007049ba7bfee4837c42a0a2b89c2fa Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 08:45:54 +0000 Subject: [PATCH 7/9] chore: drop the retired test database variable guard make check-database no longer checks for PARSAR_AGENTS_API_TEST_DATABASE_URL; it only requires OAC_TEST_DATABASE_URL. --- CONTRIBUTING.md | 2 +- Makefile | 3 --- scripts/name-allowlist.json | 15 --------------- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 60ed792fa..cd5fbe376 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -116,7 +116,7 @@ It excludes Parsar product Web and server gates. | `OAC_TEST_DATABASE_URL` | A dedicated test database. The full gate fails when it is missing. | | `OAC_TEST_OFFICIAL_SDK_PYTHON` | The pinned official SDK interpreter | -The role needs `CREATE DATABASE`: managed-provider tests create and drop isolated `oac_*_tests` databases because provider identity is deployment-wide. `PARSAR_AGENTS_API_TEST_DATABASE_URL` is retired; `make check-database` reports its replacement when only the old name is set. Tests must not bypass the production provider-switch guard. +The role needs `CREATE DATABASE`: managed-provider tests create and drop isolated `oac_*_tests` databases because provider identity is deployment-wide. Tests must not bypass the production provider-switch guard. ### Contract and schema rules diff --git a/Makefile b/Makefile index 00f637e85..38ec95f02 100644 --- a/Makefile +++ b/Makefile @@ -25,9 +25,6 @@ check-names: python3 scripts/check-names.py check-database: - @if [[ -n "$${PARSAR_AGENTS_API_TEST_DATABASE_URL+x}" && -z "$${OAC_TEST_DATABASE_URL+x}" ]]; then \ - echo 'PARSAR_AGENTS_API_TEST_DATABASE_URL was renamed; set OAC_TEST_DATABASE_URL instead' >&2; exit 1; \ - fi @test -n "$${OAC_TEST_DATABASE_URL:-}" || { echo 'Set OAC_TEST_DATABASE_URL to a dedicated test PostgreSQL database' >&2; exit 1; } sqlc-generate: diff --git a/scripts/name-allowlist.json b/scripts/name-allowlist.json index 13861d659..89fc56887 100644 --- a/scripts/name-allowlist.json +++ b/scripts/name-allowlist.json @@ -164,16 +164,6 @@ "regex": "\"agents-api-(?:session)?\"", "reason": "AgentStateKey is a persisted daemon/native-session resume identity carried on the existing machine wire contract; changing it would select another session." }, - { - "path": "Makefile", - "regex": "PARSAR_AGENTS_API_TEST_DATABASE_URL", - "reason": "R0 deliberately rejects the old test database variable with an actionable replacement message." - }, - { - "path": "CONTRIBUTING.md", - "regex": "PARSAR_AGENTS_API_TEST_DATABASE_URL", - "reason": "R0 deliberately rejects the old test database variable with an actionable replacement message." - }, { "path": "services/core/internal/store/oac_runtime_names_migration_test.go", "regex": "parsar-core-runtime@sha256:|parsar_worker /home/runtime/\\.parsar", @@ -304,11 +294,6 @@ "regex": "Parsar is not a dependency", "reason": "These exact phrases refer to the separate Parsar product, its ownership or historical source, not the OpenAgentCore brand." }, - { - "path": "CONTRIBUTING.md", - "regex": "(?:AGENTS_API_|CORE_CONSOLE_|PARSAR_)[A-Z0-9_]*\\*?|X-Parsar-Node-ID", - "reason": "These names appear in retirement documentation or instructions for an explicitly pinned historical release; current configuration uses OAC settings." - }, { "path": "docs/getting-started/operations.md", "regex": "~/\\.parsar/core", From 1ea26f8c73ceedea59ecb0c8f749fdf2be56eef6 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 08:46:51 +0000 Subject: [PATCH 8/9] chore: trim build script wording and unused native options build-core.sh and build-web.sh now say Core and Web in their messages. Drop the command-line entry of build-native-installer.mjs, which only build-native-installer-ci.mjs and its test import, and the --only option of native-harness-smoke.mjs, which native.yml never passes. --- scripts/build-core.sh | 9 ++++----- scripts/build-native-installer.mjs | 20 +------------------- scripts/build-web.sh | 6 +++--- scripts/native-harness-smoke.mjs | 6 ++---- 4 files changed, 10 insertions(+), 31 deletions(-) diff --git a/scripts/build-core.sh b/scripts/build-core.sh index 4f1663b29..c79565f8f 100755 --- a/scripts/build-core.sh +++ b/scripts/build-core.sh @@ -6,14 +6,14 @@ runtime_root="${OAC_DEV_HOME:-$HOME/.oac}" output_dir="${OAC_DEV_CORE_BUILD_DIR:-$runtime_root/build/oac-core}" for directory in "$runtime_root" "$output_dir"; do if [[ "$directory" != /* ]]; then - printf 'Agents API build directories must be absolute: %s\n' "$directory" >&2 + printf 'Core build directories must be absolute: %s\n' "$directory" >&2 exit 1 fi done revision="${OAC_DEV_BUILD_REVISION:-$(git -C "$repo_root" rev-parse HEAD 2>/dev/null || true)}" if [[ -n "$revision" && ! "$revision" =~ ^[0-9a-f]{40}$ ]]; then - printf 'Invalid Agents API source revision\n' >&2 + printf 'Invalid Core source revision\n' >&2 exit 1 fi @@ -21,8 +21,7 @@ mkdir -p "$runtime_root/cache/oac-core-builds" build_context="$(mktemp -d "$runtime_root/cache/oac-core-builds/source.XXXXXX")" trap 'rm -rf "$build_context"' EXIT -# This is the release source boundary. Product and other application sources -# must remain physically absent, even when building from the full monorepo. +# Copy only Core's source set. tar -C "$repo_root" -cf - \ go.mod go.sum \ contracts/agents-api/v1 \ @@ -47,4 +46,4 @@ mkdir -p "$output_dir" for artifact in oac-core oac-core-migrate oac-core-device oac-core-environment-key oac-node; do mv -f "$build_context/bin/$artifact" "$output_dir/$artifact" done -printf 'Standalone Agents API binaries: %s\n' "$output_dir" +printf 'Core commands: %s\n' "$output_dir" diff --git a/scripts/build-native-installer.mjs b/scripts/build-native-installer.mjs index 2ea948ffb..e8cc4a250 100644 --- a/scripts/build-native-installer.mjs +++ b/scripts/build-native-installer.mjs @@ -3,8 +3,7 @@ import { createHash } from 'node:crypto'; import { spawnSync } from 'node:child_process'; import { chmod, copyFile, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, writeFile } from 'node:fs/promises'; -import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { basename, dirname, isAbsolute, join, relative, sep } from 'node:path'; import { createRequire } from 'node:module'; export const pins = { node: '22.22.0', codex: '0.153.4', claude: '0.3.269', minimax: '0.4.12' }; @@ -179,20 +178,3 @@ export async function buildBundle(options) { return manifest; } finally { await rm(staging, { recursive: true, force: true }); } } - -if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { - try { - const options = {}; - for (let i = 2; i < process.argv.length; i += 2) { - const key = process.argv[i].replace(/^--/, ''); - if (!process.argv[i].startsWith('--') || !['daemon', 'node', 'codex', 'claude', 'minimax', 'output'].includes(key) || options[key] || !process.argv[i + 1]) throw new Error('Expected --daemon, --node, --codex, --claude, --minimax or --output with an absolute path'); - options[key] = process.argv[i + 1]; - } - const manifest = await buildBundle(options); - console.log(JSON.stringify({ status: 'built', os: manifest.os, arch: manifest.arch, components: Object.keys(manifest.components), model_requests: 0 })); - } catch (error) { - // Native output and environment values are never printed by the packager. - console.error(error.code ? 'Native bundle source is unavailable' : error instanceof SyntaxError ? 'Native component metadata is invalid' : error.message); - process.exitCode = 1; - } -} diff --git a/scripts/build-web.sh b/scripts/build-web.sh index b42de77d6..d5874ce27 100755 --- a/scripts/build-web.sh +++ b/scripts/build-web.sh @@ -9,10 +9,10 @@ export GOMODCACHE="${GOMODCACHE:-$runtime_root/cache/go-mod}" for directory in "$runtime_root" "$output_dir" "$GOCACHE" "$GOMODCACHE"; do case "$directory" in "$HOME/.oac"|"$HOME/.oac/"*) ;; - *) printf 'Core console build directories must be absolute and under ~/.oac\n' >&2; exit 1 ;; + *) printf 'Web build directories must be absolute and under ~/.oac\n' >&2; exit 1 ;; esac case "/$directory/" in - */../*|*/./*) printf 'Core console build directories must not contain dot segments\n' >&2; exit 1 ;; + */../*|*/./*) printf 'Web build directories must not contain dot segments\n' >&2; exit 1 ;; esac done @@ -32,4 +32,4 @@ tar -C "$repo_root" -cf - go.mod go.sum internal/obs/log services/web \ ) mkdir -p "$output_dir" mv -f "$build_context/oac-web" "$output_dir/oac-web" -printf 'Core console binary: %s/oac-web\n' "$output_dir" +printf 'Web binary: %s/oac-web\n' "$output_dir" diff --git a/scripts/native-harness-smoke.mjs b/scripts/native-harness-smoke.mjs index 23b83e415..1a87d969b 100644 --- a/scripts/native-harness-smoke.mjs +++ b/scripts/native-harness-smoke.mjs @@ -9,12 +9,11 @@ import { createInterface } from 'node:readline'; const options = {}; for (let i = 2; i < process.argv.length; i += 2) { const key = process.argv[i]; - if (!['--codex-binary', '--claude-runtime', '--only'].includes(key) || !process.argv[i + 1]) { - throw new Error('Expected --codex-binary PATH, --claude-runtime PATH or --only codex|claude'); + if (!['--codex-binary', '--claude-runtime'].includes(key) || !process.argv[i + 1]) { + throw new Error('Expected --codex-binary PATH or --claude-runtime PATH'); } options[key] = process.argv[i + 1]; } -if (options['--only'] && !['codex', 'claude'].includes(options['--only'])) throw new Error('Invalid --only selection'); const limit = 1024 * 1024; const failure = code => Object.assign(new Error(code), { safeCode: code }); const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); @@ -97,7 +96,6 @@ const root = await realpath(await mkdtemp(join(tmpdir(), 'oac-native-smoke-'))); const report = { platform: process.platform, arch: process.arch, model_requests: 0 }; try { for (const [name, run] of [['codex', () => codexSmoke(root)], ['claude', claudeSmoke]]) { - if (options['--only'] && options['--only'] !== name) continue; try { report[name] = await run(); } catch (error) { report[name] = { status: 'failed', code: error.safeCode ?? 'native_smoke_unavailable' }; process.exitCode = 1; } } From f09b0b055124c499db91dfaa13aa52ea17f19492 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 08:48:31 +0000 Subject: [PATCH 9/9] chore: fail the name guard on exceptions that excuse nothing check-names.py now reports every allowlist exception that excuses no retired identifier, whether it matches no file, matches only allowed text or outlived the text it covered. Remove the ten such exceptions, including the case-mismatched X-Core-Console-Actor, and two exceptions that other rules already cover. --- CONTRIBUTING.md | 1 + scripts/check-names.py | 23 +++++++++++++++++------ scripts/check-names.test.py | 20 ++++++++++++++++++++ scripts/name-allowlist.json | 30 ------------------------------ 4 files changed, 38 insertions(+), 36 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cd5fbe376..39e3287ff 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -168,6 +168,7 @@ Public project branding uses OpenAgentCore. The canonical vector mark is `docs/a - An exception covers only its matched text: an allowed repository import cannot hide a retired setting elsewhere on the line. - Keep exceptions narrow and explain the preserved contract or historical input. +- The guard also fails on an exception that excuses no retired identifier. Remove an exception together with the last text it covered. These identities stay unchanged: diff --git a/scripts/check-names.py b/scripts/check-names.py index e7a03e6a4..cacfa22b2 100644 --- a/scripts/check-names.py +++ b/scripts/check-names.py @@ -44,12 +44,17 @@ def load_rules(path: Path) -> list[ExceptionRule]: return rules -def violations(path: str, content: str, rules: list[ExceptionRule]) -> list[tuple[int, int, str]]: - allowed = [match.span() for rule in rules if fnmatch.fnmatchcase(path, rule.path) +def violations(path: str, content: str, rules: list[ExceptionRule], + used: set[int] | None = None) -> list[tuple[int, int, str]]: + """Return unexcused identifiers; add the index of each rule that excuses one to used.""" + allowed = [(index, match.span()) for index, rule in enumerate(rules) if fnmatch.fnmatchcase(path, rule.path) for match in rule.regex.finditer(content)] result = [] for match in FORBIDDEN.finditer(content): - if any(start <= match.start() and match.end() <= end for start, end in allowed): + excusing = {index for index, (start, end) in allowed if start <= match.start() and match.end() <= end} + if excusing: + if used is not None: + used.update(excusing) continue line = content.count("\n", 0, match.start()) + 1 column = match.start() - content.rfind("\n", 0, match.start()) @@ -66,6 +71,7 @@ def main() -> int: rules = load_rules(args.allowlist) names = subprocess.check_output(["git", "-C", str(args.root), "ls-files", "-z"]).split(b"\0") failures = [] + used: set[int] = set() for raw in names: if not raw: continue @@ -78,14 +84,19 @@ def main() -> int: if b"\0" in data: continue content = data.decode("utf-8") - failures.extend((name, *item) for item in violations(name, content, rules)) + failures.extend((name, *item) for item in violations(name, content, rules, used)) except (OSError, ValueError, re.error, subprocess.CalledProcessError) as error: print(f"Name guard failed: {error}", file=sys.stderr) return 2 for name, line, column, token in failures: print(f"{name}:{line}:{column}: retired identifier {token!r}") - if failures: - print(f"Name guard found {len(failures)} unapproved identifiers.", file=sys.stderr) + # An exception that excuses nothing hides nothing today but would silently allow the name later. + unused = [rule for index, rule in enumerate(rules) if index not in used] + for rule in unused: + print(f"{args.allowlist.name}: exception {rule.path} {rule.regex.pattern!r} excuses no retired identifier") + if failures or unused: + print(f"Name guard found {len(failures)} unapproved identifiers and {len(unused)} unused exceptions.", + file=sys.stderr) return 1 print("OpenAgentCore name guard passed.") return 0 diff --git a/scripts/check-names.test.py b/scripts/check-names.test.py index 3ce9e8608..34bf42705 100644 --- a/scripts/check-names.test.py +++ b/scripts/check-names.test.py @@ -86,6 +86,26 @@ def test_repository_scan_uses_tracked_text_and_skips_binary_without_following_li (root / "tracked.txt").write_text("OAC_RUNTIME_HOME\n") self.assertEqual(subprocess.run(command, capture_output=True).returncode, 0) + def test_exception_that_excuses_no_identifier_fails_the_scan(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + subprocess.run(["git", "init", "-q", directory], check=True) + (root / "tracked.txt").write_text("PARSAR_HOME X-Core-Console-Actor\n") + subprocess.run(["git", "-C", directory, "add", "tracked.txt"], check=True) + rules = root / "rules.json" + used = {"path": "tracked.txt", "regex": "PARSAR_HOME", "reason": "Excuses the setting"} + command = [sys.executable, str(Path(names.__file__)), "--root", directory, "--allowlist", str(rules)] + for unused in ({"path": "missing/*", "regex": "parsar", "reason": "Matches no file"}, + {"path": "*", "regex": "X-Core-Console-Actor", "reason": "Matches only allowed text"}): + with self.subTest(unused=unused["regex"]): + rules.write_text(json.dumps([used, unused])) + result = subprocess.run(command, text=True, capture_output=True) + self.assertEqual(result.returncode, 1) + self.assertIn(repr(unused["regex"]), result.stdout) + self.assertNotIn("PARSAR_HOME", result.stdout) + rules.write_text(json.dumps([used])) + self.assertEqual(subprocess.run(command, capture_output=True).returncode, 0) + def test_persisted_domain_exception_does_not_allow_other_settings_or_paths(self): rules = names.load_rules(Path(__file__).with_name("name-allowlist.json")) content = '"parsar.agents-api.credential"; "PARSAR_HOME"' diff --git a/scripts/name-allowlist.json b/scripts/name-allowlist.json index 89fc56887..8dccb5db3 100644 --- a/scripts/name-allowlist.json +++ b/scripts/name-allowlist.json @@ -4,11 +4,6 @@ "regex": "contracts/agents-api(?:/[A-Za-z0-9_.*-]+)*", "reason": "The Agents API contract directory is an existing source path, not an installed executable." }, - { - "path": "*", - "regex": "~?/\\.parsar/remediation/[A-Za-z0-9_./-]+", - "reason": "Historical remediation artifacts retain their evidence paths." - }, { "path": "packages/agents-client/src/*", "regex": "(?:\\./)?fixtures/parsar-[0-9a-f]+/[A-Za-z0-9_./-]+", @@ -44,11 +39,6 @@ "regex": "(?i)parsar|\\bAGENTS_CORE_WEB_[A-Z][A-Z0-9_]*|\\bAGENTS_API_[A-Z][A-Z0-9_]*|\\bCORE_CONSOLE_[A-Z][A-Z0-9_]*|\\bagents-api(?:-(?:migrate|device|environment-key|e2b-provider|microsandbox-provider|tool-root|codex-directory|codex-write|workspace-export|runtime-initialize|claude-shell-prefix))?\\b|\\bcore-console\\b|\\bagents-runtime-|\\bAgents? Core(?: Web)?\\b|@agents-core-web/", "reason": "Guard regression fixtures intentionally contain retired identifiers, including rejected examples." }, - { - "path": "*", - "regex": "X-Core-Console-Actor", - "reason": "This existing HTTP header is explicitly retained; it is a wire contract, not the Web binary name." - }, { "path": "*", "regex": "\\bPARSAR_(?:CAPABILITY_UPLOAD_TOKEN|SERVER_URL|MASTER_KEY|PROTOCOL_BASELINE_REVISION)\\b", @@ -64,11 +54,6 @@ "regex": "(?:scripts/)?extract-agents-api-upstream\\.py", "reason": "The upstream contract extraction helper names the pinned OpenAI Agents API protocol, not an installed command." }, - { - "path": ".github/workflows/api-acceptance.yml", - "regex": "\\.github/workflows/agents-api\\.yml", - "reason": "The workflow source filename is an internal repository identity; its display name and artifacts use OpenAgentCore." - }, { "path": "*", "regex": "https://developers\\.openai\\.com/api/docs/guides/agents-api(?:/[A-Za-z0-9_./-]*)?", @@ -174,11 +159,6 @@ "regex": "PARSAR_APPLICATION_VALUE", "reason": "This test proves the old reservation no longer blocks application-provided product variables." }, - { - "path": "services/core/tools/e2b-provider/provider.py", - "regex": "/opt/parsar-e2b", - "reason": "The provider detects an old E2B template and refuses it before writing credentials." - }, { "path": "services/core/migrations/000075_public_url_binding.sql", "regex": "AGENTS_API_PUBLIC_URL", @@ -189,11 +169,6 @@ "regex": "\\.parsar/core|\\bparsar\\b", "reason": "These old default paths and command names are used only by conversion refusal or the explicit retired-command stub." }, - { - "path": "deploy/install/test_oac.py", - "regex": "\\.parsar/core|\\bparsar\\b", - "reason": "These old default paths and command names are used only by conversion refusal or the explicit retired-command stub." - }, { "path": "deploy/install/oac_cli.py", "regex": "\\.parsar/core|\\bparsar\\b", @@ -339,11 +314,6 @@ "regex": "example/parsar/", "reason": "The explicitly named Parsar application example retains product branding; it does not rename the Core runtime." }, - { - "path": "README.md", - "regex": "example/parsar(?:/README.md)?", - "reason": "The explicitly named Parsar application example retains product branding; it does not rename the Core runtime." - }, { "path": "apps/daemon/internal/cli/connect_cleanup_test.go", "regex": "\"agents-api-cleanup\"",