Skip to content

fix: State the named-only filter and unnamed retention in storage lists - #1567

Merged
jirispilka merged 5 commits into
masterfrom
fix/storage-list-named-filter
Oct 15, 2026
Merged

jirispilka merged 5 commits into
masterfrom
fix/storage-list-named-filter

Conversation

@jirispilka

@jirispilka jirispilka commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Why

Closes apify/ai-team#335

Review page with before/after examples for this PR and the five related ones (#1563–#1566, #1568): https://claude.ai/artifact/WDRj1nwPnMTP9ktGyg3z3i

get-dataset-list and get-key-value-store-list default to unnamed=false, so they list only named storages, while Actor runs write to unnamed ones. The response did not say the list was limited, and an empty page pointed at inspecting an entry of the empty list. Nothing served said how long unnamed storages are kept.

What changed

Summary with unnamed=false (the default) says "named"; with unnamed=true it is unchanged. On an empty page with no next page, nextStep states what unnamed=true adds instead of pointing at the empty list. Pagination hints and the hint on a nonempty last page are unchanged; tool descriptions and defaults are unchanged.

get-dataset-list call (test account: 11 named, 855 unnamed datasets) Before Now
defaults Listed 10 of 11 datasets. Listed 10 of 11 named datasets.
unnamed=true Listed 10 of 855 datasets. unchanged
empty page, defaults Listed 0 of 11 datasets. / Use get-dataset with a datasetId from the list to inspect a dataset. Listed 0 of 11 named datasets. / unnamed=true includes the unnamed datasets that Actor runs create.
empty page, unnamed=true Listed 0 of 855 datasets. / Use get-dataset with a datasetId from the list to inspect a dataset. Listed 0 of 855 datasets. / No follow-up step.

get-key-value-store-list does the same with "key-value stores". The server instructions' ## Storage types section gets one bullet:

- Unnamed storages are deleted after the account's data-retention period; named storages are kept until deleted.

The new text names no tool and asks for no call. This is a tool-text change to structuredContent.summary/nextStep of both list tools and to the server instructions.

Notes for reviewers (human-written)

Proof it works

mcpc with the Claude connector tool set; counts checked with apify datasets ls / apify key-value-stores ls (with and without --unnamed). The table above is this probe: "Before" on a build with master's storage code, "Now" on this branch. Empty pages use an offset past the end, so no storage was created or deleted.

get-key-value-store-list on the same account (18 named, 789 unnamed stores):

Call summary nextStep
defaults Listed 10 of 18 named key-value stores. Call get-key-value-store-list again with offset=10 to fetch the next page.
unnamed=true Listed 10 of 789 key-value stores. Call get-key-value-store-list again with offset=10 to fetch the next page.
offset=100000 Listed 0 of 18 named key-value stores. unnamed=true includes the unnamed key-value stores that Actor runs create.
offset=100000, unnamed=true Listed 0 of 789 key-value stores. No follow-up step.

content[1].text is summary + newline + nextStep, and the retention bullet is in the served instructions.

Unit tests: the named summary on default pages, both empty-page nextStep variants (including an offset past the end) with schema validation against storageListOutputSchema, and the retention bullet in ## Storage types fail before the fix. Guards keep the unnamed=true summary and the inspect hint on a nonempty last page unchanged.

Implemented with Claude Code: code, tests, mcpc probe and this description. Reviewed by a separate Claude Code review pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC


Generated by Claude Code

get-dataset-list and get-key-value-store-list now say "named" in the
summary when unnamed=false. An empty last page states what unnamed=true
adds (or "No follow-up step." with unnamed=true) instead of pointing at
an empty list. The server instructions state that unnamed storages are
deleted after the data-retention period.

Closes apify/ai-team#335

Claude-Session: https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC
@jirispilka
jirispilka marked this pull request as ready for review October 14, 2026 08:43
@apify-service-account apify-service-account added the tested Temporary label used only programatically for some analytics. label Oct 14, 2026
@apify-service-account apify-service-account added the t-ai Issues owned by the AI team. label Oct 14, 2026

Copy link
Copy Markdown
Collaborator Author

PR title check failed on ffc91f8 before checking anything: amannn/action-semantic-pull-request exited with "other side closed", a dropped connection to the GitHub API. The title is unchanged and passed on earlier commits. Not this PR's; I'll re-run the failed job once the rest of the workflow run finishes.


Generated by Claude Code

…med-filter

Conflict in tests/unit/server-instructions.test.ts: kept master's task and
schedule tests and moved the Storage types retention test after them, using
master's sectionOf helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPzYK17FoL2F3cuVonqnej

@vojtechj-apify vojtechj-apify left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, two test nits inline.

Comment thread tests/unit/tools.get_dataset_list.test.ts Outdated
Comment thread tests/unit/tools.get_dataset_list.test.ts
Review feedback: a page with count 0, total 5 at offset 0 needs limit=0,
and the hint points the agent back at the page it just got.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPzYK17FoL2F3cuVonqnej
…med-filter

Conflicts in the dataset and key-value store list tests: kept this branch's
named/empty-page tests and master's limit validation tests from #1582, which
replace the old "rejects limit above N" test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPzYK17FoL2F3cuVonqnej
jirispilka added a commit that referenced this pull request Oct 15, 2026
…1568)

## Why

Closes #1276

Review page with before/after examples for this PR and the five related
ones (#1563–#1567): https://claude.ai/artifact/WDRj1nwPnMTP9ktGyg3z3i

Reported by @lokicik. This follows the implementation spec in the
maintainer comment on the issue.

`search-actors` returned the no-match guidance ("Keywords can include …
A platform-only query such as "TikTok" …") for any empty page, including
a page past the last result of a query that matched. With an empty query
that advice cannot help, and it reads as if nothing matched.

## What changed

`buildNoActorsFoundInstructions()`, shared by `search-actors` and
`search-actors-widget`, now gets the offset. With `offset > 0` it
returns one sentence and no keyword advice; at `offset` 0 the text is
unchanged. No new response fields; the output schema is unchanged. This
is a tool-text change to `structuredContent.instructions` and the text
content.

`search-actors` with `keywords: "", limit: 1, offset: 1000000`:

```
Before: No Actors were found for the search query "".
        Keywords can include the named platform and/or data type (e.g. "TikTok posts").
        A platform-only query such as "TikTok" is an option if more specific results do not fit.
Now:    No Actors past offset 1000000 for the search query "".
```

## Notes for reviewers (human-written)

## Proof it works

mcpc with the Claude connector tool set on this branch:

| `search-actors` call | Result |
|---|---|
| `keywords: "", limit: 1, offset: 0` | 1 Actor |
| `keywords: "", limit: 1, offset: 1000000` | `No Actors past offset
1000000 for the search query "".` |
| `keywords: "instagram scraper", limit: 5, offset: 5000` | `No Actors
past offset 5000 for the search query "instagram scraper".` |
| `keywords: "zzqqxxnotarealactorkeyword123", offset: 0` | the
three-line no-match guidance, unchanged |

The text content equals `structuredContent.instructions`, and
`structuredContent` keeps its fields (`actors: []`, `query`, `count: 0`,
`instructions`). The same `offset: 1000000` call on a build with
master's text returns the three-line guidance shown under "Before".

Unit tests (failing before the fix): an empty page at `offset` 1 returns
the new sentence in `structuredContent` and the text content, for both
`search-actors` and `search-actors-widget`. The existing
empty-first-page tests keep the three-line text.

Implemented with Claude Code: code, tests, mcpc probe and this
description. Reviewed by a separate Claude Code review pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC

---
_Generated by [Claude
Code](https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
@jirispilka
jirispilka merged commit 05b7586 into master Oct 15, 2026
16 checks passed
@jirispilka
jirispilka deleted the fix/storage-list-named-filter branch October 15, 2026 10:33
jirispilka added a commit that referenced this pull request Oct 15, 2026
## Why

Closes #1560

Review page with before/after examples for this PR and the five related
ones (#1563–#1565, #1567, #1568):
https://claude.ai/artifact/WDRj1nwPnMTP9ktGyg3z3i

Aborting a run that had already finished returns it unchanged, and
`abort-actor-run` reported it like any status read ("SUCCEEDED in 68s.
…"), with no word that the abort did nothing. An already-ABORTED run
looked the same as an abort that took effect.

## What changed

`abort-actor-run` reads the run before aborting. When the status is
SUCCEEDED, FAILED, TIMED-OUT or ABORTED, it sends no abort and `summary`
starts with "Nothing was aborted: the run had already finished." Any
other status is aborted as before, and the returned ABORTING or ABORTED
text is unchanged. `nextStep` and the output schema are unchanged. This
is a tool-text change to `structuredContent.summary` and the narrative
text.

`abort-actor-run` on an ABORTED run:

```
Before: ABORTED after 9.612s. Actor status: "Finished! Total 1 requests: 1 succeeded, 0 failed".
Now:    Nothing was aborted: the run had already finished. ABORTED after 9.612s. Actor status: "Finished! Total 1 requests: 1 succeeded, 0 failed".
```

An unknown `runId` now returns the text `get-actor-run` uses, from the
read instead of the abort:

```
Before: Error calling tool "abort-actor-run": Actor run was not found (API error type: record-not-found).
Now:    Run with ID 'doesNotExist123' not found.
```

Aborting a running run now takes two API calls (read, then abort); a
finished run takes one, as before. If a run finishes between the read
and the abort, the response shows its final status without the prefix.
`structuredContent` keeps its shape; only the `summary` of a finished
run and the not-found text change, which matters if the hosted server's
contract tests pin either.

## Differs from #1560

The issue proposed checking the status the abort call returns, prefixing
only SUCCEEDED, FAILED and TIMED-OUT, and keeping today's text for
ABORTING and ABORTED. This PR does three things differently:

- **Reads the run before the abort.** The abort response for a run that
was already ABORTED looks the same as one for a run this call just
aborted, so only a read beforehand can tell them apart. The cost is one
extra API call per abort of an unfinished run.
- **Prefixes already-ABORTED runs too.** Nothing was aborted for them
either.
- **Changes the not-found text.** It now comes from the read, so it
matches `get-actor-run`.

## Notes for reviewers (human-written)

## Proof it works

mcpc with the Claude connector tool set, `abort-actor-run` on finished
runs from the account (`get-actor-run` before and after shows the same
status and `finishedAt`; the calls changed nothing):

| Run | Status | `structuredContent.summary` |
|---|---|---|
| `Nz8MuAIqzMUiusK4A` | SUCCEEDED | Nothing was aborted: the run had
already finished. SUCCEEDED in 68.317s. Actor status: "Finished! Scraped
2 pages, 0 failed". |
| `SCbQwsYRhX8vmarD3` | FAILED | Nothing was aborted: the run had
already finished. FAILED after 96.95s. |
| `O3KXkXfnQfLp8X13n` | ABORTED | Nothing was aborted: the run had
already finished. ABORTED after 9.612s. Actor status: "Finished! Total 1
requests: 1 succeeded, 0 failed". |
| `b08qGCehNYxW69XI4` | ABORTED | Nothing was aborted: the run had
already finished. ABORTED after 12.799s. Actor status: "Starting the
crawler". |
| `doesNotExist123` | – | `isError: true`, `Run with ID
'doesNotExist123' not found.` |

`content[1].text` starts with the same summary, and `content[0]` equals
`structuredContent`. The probe started no run, so aborting a running run
is covered by unit tests only.

Unit tests (failing before the fix): for SUCCEEDED, FAILED, TIMED-OUT
and ABORTED runs no abort is sent and the summary starts with the
sentence, `content[0]` mirrors `structuredContent`, and the response
validates against `actorRunOutputSchema`; an unknown run returns the
not-found error without an abort. READY, RUNNING, TIMING-OUT and
ABORTING runs are aborted once, and an ABORTING or ABORTED response
keeps its summary.

Implemented with Claude Code: code, tests, mcpc probe and this
description. Reviewed by a separate Claude Code review pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC

---
_Generated by [Claude
Code](https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-ai Issues owned by the AI team. tested Temporary label used only programatically for some analytics.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants