Repository navigation
Conversation
Move the files input rules, the manifest builder, and the missing Dockerfile warning from create-actor to source_helpers.ts, so create-actor-version can use the same rules. create-actor behaves as before.
create-actor-version adds a version to one of your own Actors with one POST: a copy of another version, new files, a Git repository, or an empty version. A copy reads the other version and sends its source as stored, so file formats, folder entries, and credentials stored with a Git or gist URL are kept and no content passes through the model. Non-secret env vars are copied with their values; secret ones cannot be read, so their names are returned. Zip-stored versions cannot be copied yet. buildTag has no default, so a working copy never takes over latest. delete-actor-version reads the Actor first, refuses a version it does not have, and refuses its last version, also when the platform does. The version's builds and their tags stay. Both tools are in the source category.
- The refusal for a zip-stored source version no longer suggests a bare apify push --version, which takes the tag from .actor/actor.json (often latest) and builds right away. It points to files or gitRepoUrl, and gives the CLI command with a build tag of its own. - The refusal for an existing versionNumber leads with a free number after the highest version. update-actor-version is offered only for a caller who meant that version. After a race, the list includes the requested number. - The result warns when the new version turns Standby on for the whole Actor: the platform does that when .actor/actor.json sets usesStandbyMode and Standby is off. - update-actor-version's refusal of a missing version names create-actor-version when the session has it. - Share the Git or gist source text, the call size check for new files, the sent files warnings, and the path, duplicate, and entry rules for files and replaceFiles through source_helpers.ts. - Type the version POST body instead of casting it. - Mark the delete-actor pointer for the last version as TODO(#1439). - Tests: the copy test compares against its own copy of the stored files, and the input schema checks match tool names with any punctuation, in delete-actor-version too.
…feat/actor-version-create-delete # Conflicts: # src/tools/source/create_actor.ts # src/tools/source/source_helpers.ts
…feat/actor-version-create-delete
A copy reads every file of the source version and sends them all back, because the Apify API cannot copy a version or apply atomic changes to an Actor's source. A TODO marks the place for when it can.
…te-delete # Conflicts: # src/tools/AGENTS.md # src/tools/source/source_helpers.ts
…te-delete # Conflicts: # src/tools/source/create_actor.ts
…te-delete Take the simplified get-actor-version, create-actor, and update-actor-version, and move create-actor-version and delete-actor-version onto the base helpers: fetchActor and resolveVersion instead of the own-account check, extractSourceFiles for copies, buildSourceFileEntry for sent files, and respondAfterWrite for the build. Copies of Git, gist, and zip versions, gitRepoUrl, and the file rules the base dropped go with those helpers.
create-actor-version takes files or copyFromVersion and sends one POST. A copy takes the other version's stored files, its non-secret env vars, and applyEnvVarsToBuild from the Actor GET, never its build tag, and is refused for a version not stored as files. The version-exists check and its rewording, the Standby warning, the secret env var list, empty-version handling, and the summary variants are gone; the platform's own errors reach the caller. delete-actor-version keeps the Actor read before the DELETE, since apify-client reports a delete of a missing version as done, and leaves the last-version refusal to the platform.
…-version simplification create-actor-version now refuses a call with neither files nor copyFromVersion before any request, as it already did for a call with both. The platform accepts an empty file list and creates an empty version, and with autoBuild it would also build that version, which the tool does not offer.
…te-delete # Conflicts: # README.md # src/tools/registry.ts
…d the files manifest in create-actor-version
…d no other write in delete-actor-version
… the build start in create-actor-version
create-actor-version built the entries of a version from files with no check, so a path outside the Actor root, a folder path, the same path twice, a file under another file, text sent as base64, and text with a lone UTF-16 surrogate were POSTed and reported as created. The platform stores them, and the build then fails or writes a corrupt file. create-actor-version now builds the entries with the checks create-actor runs, moved to source_helpers.ts, and refuses before the POST. A copy still sends the other version's entries as stored.
…e version apify-client retries the version POST after a network error, a timeout, a 429, or a 5xx. When the platform saved an attempt it did not answer, the retry fails with version-already-exists, and create-actor-version reported the version it had just created as taken. A refusal of the number on a retry now says that an earlier attempt may have created the version and that it should be read before calling create-actor-version again, as create-actor does for a name.
…ut the token hint The platform refuses a version number the Actor already has (version-already-exists) and the deletion of an Actor's last version (too-few-versions) with a 403. create-actor-version and delete-actor-version rethrew both, so the tool-call engine added "The resource may be private or your token may lack access." and recorded an AUTH failure. Both refusals now come back as a soft failure with the platform's message and error type, and no hint. Other API errors still go to the engine.
With buildTag and autoBuild, the build it starts moves an existing tag, as build-actor does.
The 403 mapping is shared by the source tools; the PUT is the write most likely to hit the size limit.
The platform's too-few-versions refusal now reads as the Actor's only version. Only delete-actor-version can get it, so the shared source tool mapping no longer lists it.
When given, the delete is refused before the DELETE unless the version's files have that revision, computed as get-actor-version and the write tools compute it and compared ignoring case. A version not stored as files has no revision, so expectedRevision is refused for it. A null or empty expectedRevision counts as not given.
structuredContent gets buildTag, left out when the version has none, and the summary names it, so a caller can tell which version builds as latest and which as beta.
…-version structuredContent gets tags, the tags its text already names, as an array that is empty when no tag points to a build of the version.
…ate tools create-actor and create-actor-version built the same result after the POST and the same refusal of a taken name or number on a retried POST. Both now come from source_helpers.ts, with the served text unchanged.
…sertion A type guard on the check that the call gives exactly one of files and copyFromVersion types files as defined when copyFromVersion is not given.
| throw new UserInputError(`${label} repeats the path ${entry.name}; each path can be sent only once.`); | ||
| } | ||
| const folders = listFolders(entry.name); | ||
| const collision = folders.find((folder) => filePaths.has(folder)) ?? firstFileByFolder.get(entry.name); |
There was a problem hiding this comment.
from codex:
Suggestion, separate follow-up: validateNewFilePath and buildSourceFileEntries both enforce file-versus-folder collisions. The former copies and scans the paths for each new file; the latter uses a set and map. Could we share the collision-checking logic while preserving ordered update operations and explicit folder entries? I haven’t found a correctness failure in either implementation.
There was a problem hiding this comment.
Again, sorry, did not have time to go through it all.
Did some testing with an agent only 😊
Tested #1454 at 8ac7274 end to end through mcpc, using GPT-6 Luna as the testing agent and the Apify CLI to verify platform state.
Creation, reading, copying, write/edit/delete operations, revision and hash checks, version deletion, tag retention, and last-version protection passed. Failed update preconditions left the stored files unchanged. Real builds and an Actor run also succeeded.
Two observations:
- An unchanged update with autoBuild: true starts a build. This reproduces the no-op behavior already raised on #1453.
- call-actor with an explicit callOptions.build failed with unknown-build-tag for latest when no latest build existed. A default call succeeded after creating that build. This needs separate investigation; it does not establish a defect in these source tools.
The source category is retired and loads no tools, so these cases would get none of the tools they call.
The merge moved the tools to the versions folder, so the output schema import now sorts before them.
What
Two tools in the
versionscategory (renamed fromsourcein #1453):create-actor-versionadds a version to an existing Actor, from files or as a copy of another version (copyFromVersion).delete-actor-versiondeletes a version.Why
The API's version update cannot create a version, so without this tool an agent cannot add
0.2next to0.1. A copy gives an agent a working version to change and build without touching the one in use. Part of the Actor source tools spec (#1452). Closes #1475.How
create-actor-version:actor,versionNumber, eitherfilesorcopyFromVersion,buildTag, andautoBuild. One POST creates the version.applyEnvVarsToBuild. The files go back exactly as the API returned them, folders and entries without a format or content included. The API does not return secret values, so secrets are left out, andwarningsnames them. WithoutautoBuild, the warning says they can be set on the new version in Apify Console, and that only a build made after that includes them. WhenautoBuildstarted a build, the tool keeps it, and the warning says that build runs without them and, if it succeeds, takes thebuildTagwhen one was given. Only versions stored as files can be copied.buildTaghas no default and is never copied, so builds of a working copy never move a tag such aslatest.actorargument of both tools takes an ID, a full name, or a bare name resolved against your own Actors, through the sharedfetchActorfrom feat!: Add create-actor and update-actor-version tools #1453.create-actor: a path with a control character (U+0000 to U+001F or U+007F) or a lone UTF-16 surrogate, a path outside the Actor root or at a folder path, the same path twice, one path used as both a file and a folder, base64 that does not decode without loss, and text with a lone UTF-16 surrogate. The platform accepts all of these, and the build then fails or stores a corrupt file.autoBuild(default false) starts a build without waiting for it. A build that cannot start is reported inbuildError, and the version stays. A cancel that arrives after the POST, including during the Console link lookup before the build start, keeps the version and starts no build.create-actor-versionhasdestructiveHint: trueandopenWorldHint: true: withbuildTagandautoBuild, the build it starts moves an existing tag, asbuild-actordoes.delete-actor-versionstarts nothing and keepsopenWorldHint: false.create-actor-versionsetsredactArgsascreate-actordoes. The logged copy keeps the Actor,versionNumber,copyFromVersion,buildTag,autoBuild, and each file's path and encoding, and replaces file content with[REDACTED].delete-actor-versiontakes no file content.get-actor-version, which reads the files, only when the session has that tool. For a copy, it names the files of the copied version, not the files the call sent, and it repeats the warning about the secret env vars that were left out.delete-actor-version:actor,versionNumber, and an optionalexpectedRevision. WhenexpectedRevisionis given, the delete is refused unless the version's revision matches, compared asupdate-actor-versioncompares it. A version not stored as files, or one whose source the API hides, has no revision, so the delete is then refused. It reads the Actor first, because apify-client treats a 404 on delete as success. The version's builds and their tags stay. The tool description says a deleted version cannot be restored, that a build of the version that is still running keeps running and takes its tag if it succeeds, and to delete a version only when the user asks.structuredContent.tagslists the tags that point to a build of the deleted version, empty when none. When a tag points to a build of the deleted version, the reply also names the tag and says runs with it still use that build until the tag points to another build, such as one of another version made with that tag. With several such tags, it names one build per tag, sincebuild-actortakes one tag. It namesbuild-actoronly when the session has that tool. The tool reads where each tag points from the Actor it already fetched, and matches the build number on the version number followed by a dot, so deleting0.1does not name a tag on build0.10.1. It does not use the version's build tag: a tag stays on the build it was last given to, which can be a build of another version.TOOL_GUIDELINES.mdand states facts: for example, the files-or-copy refusal says "The call takes exactly one of files and copyFromVersion."get-actor-version(on master) returns the version'sbuildTaginstructuredContent, and its summary names it, for example "Read version 0.2 (build tag beta) of john/my-actor." Without it, an agent comparing two versions could not tell which onelatestbuilds.buildSourceFileEntriesmoves fromcreate_actor.tstosource_helpers.ts, so both create tools share it. So dorespondAfterCreate(the result after the POST) andrespondToCreateError(the refusal of a retried POST). It checks paths with a set and a map (from feat!: Add create-actor and update-actor-version tools #1453), so 20,000 files take milliseconds, not 8 s.[path, hash]pairs, sorted by path, so it still matches whatget-actor-versionreads.Testing
Unit tests cover versions from files and copies (the same manifest and revision as
get-actor-version, the copied entries, env vars, and no build tag), refused files and source types, the files-or-copy rule,autoBuildandbuildError, a taken version number on the first try and on a retry, the version limit and the size limit (the platform's message and error type, status 403, no token hint), the last-version refusal, a permission error passed on to the engine, a missing version on delete, cancellation, and schema conformance. A cancel during the POST is tested withautoBuild. They also cover the secret env var warning withoutautoBuild, after a build that could not start, and after a started build with and withoutbuildTag, and no warning for a copy without secrets. The reply to a retried POST is tested for files, for a copy with secrets, and for a copy without. They cover namingget-actor-versionandbuild-actoronly when the session has them, the note about tags after a delete, and the tool description's lines on restoring a version, on a running build taking its tag, and on deleting only when asked. The delete cases include a tag that moved to a build of another version, a tagged version that was never built, a version without a build tag whose build got a tag frombuild-actor, two tags on builds of the deleted version, and a tag on build0.10.1when deleting version0.1. For the reviews of #1453, they also cover the logged arguments with a synthetic secret in file content and unknown keys, control characters and a lone UTF-16 surrogate in file paths, a cancel during the Console link lookup, 20,000 files checked within a second, a bare Actor name, and the open-world annotation. For the review of this PR, they coverdestructiveHint,expectedRevisionon delete (a match, upper case, a mismatch, a version not stored as files, a hidden source, andnull), thetagsandbuildTagoutputs, the last-version text, andversions-size-exceededon theupdate-actor-versionPUT.type-check,lint,format,test:unit, andcheck:agentspass.Live integration cases in
tests/test_kit/cases/versions.cases.tsrun in CI against the platform on all three transports (stdio, streamable HTTP, and stateless HTTP):create-actor-versionfrom files, thenget-actor-versionreading back the same hashes and revision, with no build tag on the new version and the other version unchangedapplyEnvVarsToBuild, and no build tag, with a warning that names the secret env var but not its value, then the same call refused as takendelete-actor-versiondeleting a version that has no builds, with no note about tags, then refusing a version the Actor no longer has, and the platform refusing to delete the last oneEach case uses its own
test-source-*Actor, deletes it at the end, and starts no build.Notes
create-actorandupdate-actor-version(feat!: Add create-actor and update-actor-version tools #1453). This is the top of the stack.get-actor-version, which is on master: a new optionalbuildTagin its output schema andstructuredContent, and a summary that names the build tag. The hosted server readsstructuredContent, so its contract tests may need the field. The other changes here are to the new tools.create-actor,create-actor-version, orupdate-actor-versioncall gets a 413 before the tool runs. fix: Accept request bodies up to 9 MB in the dev server #1611 (dev server) and apify/apify-mcp-server-internal#842 (hosted server) raise it to 9 MiB, tracked in HTTP servers refuse request bodies over 100 KB #1610; the internal one has to ship before these tools do.AI disclosure: implemented with Claude Code; awaiting human review.
🤖 Generated with Claude Code