Skip to content

feat: Warn when a discovered Actor cannot be run in this session - #1327

Draft
MQ37 wants to merge 6 commits into
fix/gate-call-actor-when-absentfrom
feat/discovery-guidance-actor-runnable
Draft

feat: Warn when a discovered Actor cannot be run in this session#1327
MQ37 wants to merge 6 commits into
fix/gate-call-actor-when-absentfrom
feat/discovery-guidance-actor-runnable

Conversation

@MQ37

@MQ37 MQ37 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🚨 Blocker: not mergeable yet — the guidance/nudge text below ("open its Apify page or configure it separately") should link to a docs page explaining how to configure an Actor for use, but that page isn't deployed yet. Update the nudge text to point at it once live.

Closes apify/ai-team#231

Stacked on #1326, which is stacked on #1334 — base branch is #1326's, not master; diff here is just this PR's own commits.

What

search-actors and fetch-actor-details (+ widget variants) now say something when a found Actor can't actually be run in the current session — neither call-actor nor a dedicated tool for it is loaded.

Why

Previously silent: the model was left to guess or hallucinate a call for an Actor that showed up in search results or details but has no runnable path in this configuration.

  • actor_tool_naming.ts: new canRunActor(actorId, loadedToolNames, loadedActorIds) — true when call-actor is loaded or the Actor's ID is among the session's loaded Actor/Actor-MCP tools. Matches by stable Actor ID, not by reconstructing a tool-name string, so it's immune to username-length capping and to -- inside Actor names. Soft check for a guidance hint, not a hard gate.
  • search_actors.ts: per-result check — the caveat fires only when at least one returned Actor actually lacks a run path, not whenever call-actor is absent.
  • fetch_actor_details.ts: per-Actor guidance on the success path, mirroring buildActorNotFoundResponse's existing loadedToolNames gating pattern for the not-found case; matches by the API-resolved Actor ID directly, so querying by ID or username/name resolves identically.
  • Both widget variants build their own response text and needed their own gated append.

Testing

canRunActor covered directly (call-actor, dedicated tool, Actor-MCP sub-tool, ID-vs-full-name), the per-result search-actors caveat (single result, mixed results, all-already-loaded), guidance-present/absent for both tools and both widget variants. pnpm run type-check / lint / test:unit / format / check:agents all green (1595 tests).

@MQ37
MQ37 force-pushed the fix/gate-call-actor-when-absent branch from cb57d0a to cdf450f Compare September 3, 2026 13:16
@MQ37
MQ37 force-pushed the feat/discovery-guidance-actor-runnable branch from f14643f to 0a9331e Compare September 3, 2026 13:18
@MQ37
MQ37 force-pushed the fix/gate-call-actor-when-absent branch from cdf450f to 67c2372 Compare September 3, 2026 13:54
@MQ37
MQ37 force-pushed the feat/discovery-guidance-actor-runnable branch from 0a9331e to f94690a Compare September 3, 2026 13:59
@MQ37
MQ37 force-pushed the fix/gate-call-actor-when-absent branch from 67c2372 to a2c9a6d Compare September 3, 2026 15:15
@MQ37
MQ37 force-pushed the feat/discovery-guidance-actor-runnable branch 4 times, most recently from 5fe2e7b to 1a71253 Compare September 4, 2026 08:29
@MQ37
MQ37 force-pushed the fix/gate-call-actor-when-absent branch from d61b36f to 8d0396b Compare September 4, 2026 14:30
MQ37 added 5 commits September 4, 2026 16:32
search-actors and fetch-actor-details never said anything when
neither call-actor nor a dedicated tool was loaded for a found
Actor \u2014 the model was left to guess or hallucinate a call.

- actor_tool_naming.ts: canRunActor(actorFullName, loadedToolNames)
  \u2014 true when call-actor is loaded or the Actor has its own
  dedicated tool. Soft check for a guidance hint, not a hard gate;
  known false negative for a hash-capped MCP-proxy tool name.
- search_actors.ts: session-level caveat (call-actor absent means
  no result in the list can run, not just some).
- fetch_actor_details.ts: per-Actor guidance on the success path,
  mirroring buildActorNotFoundResponse's existing loadedToolNames
  gating pattern for the not-found case.
- Both widget variants build their own text and needed their own
  gated append.
An Actor MCP server registers each sub-tool as {actor}--{originTool},
never the bare actor tool name, so the exact-match check was always
false for that Actor type -- not just for a hash-capped name as the
docstring claimed.
The prior prefix-match approach was unsound in both directions: username
capping made it false-negative for any Actor MCP server whose sibling
tool names pushed it over the length cap (common, not the rare case the
docstring claimed), and "--" isn't excluded from Actor names, so it could
false-positive-match a different Actor's tool.

InternalToolArgs now carries loadedActorIds (Actor IDs behind every loaded
Actor/Actor-MCP tool, built once from the existing tool registry via the
existing extractActorId helper). canRunActor matches against that set
directly -- exact, and immune to the naming scheme entirely.

fetch-actor-details passes the resolved Actor ID rather than reconstructing
a full name, which also removes the need for any username/name join.
The caveat fired whenever call-actor was absent, regardless of whether any
returned Actor already had its own dedicated tool loaded -- a session
where every result happened to be already loaded still got told none of
them could run. Now checks canRunActor per result (matches ai-team#231's
spec: guidance only when at least one result actually lacks a run path).
- canRunActor() doesn't distinguish Actor vs Actor-MCP tool types, so
  the Actor-MCP case duplicated the dedicated-tool one.
- fetch-actor-details' ID-vs-full-name case duplicated the dedicated-tool
  case: fetchActorDetails is mocked in this file, so the raw actor input
  never reaches real resolution -- both passed identical inputs.
- buildActorNotRunnableGuidance()'s dedicated-tool case duplicated what
  canRunActor()'s own tests already prove; the wrapper only needs one
  true-branch example plus the false-branch one.
- Claude-connector tool-surface case in mcp.server.stateless_instructions
  duplicated an earlier case (same code path, verified identical output)
  and built a DEFAULT-mode server despite claiming to test the apps-mode
  connector. Removed.
- report-problem-via-URL case used a tools= value that never puts
  report-problem in the candidate set, so the filter it claimed to test
  never ran. Repointed to ?tools=dev, which does.
- Merged two server-instructions.test.ts pairs that recomputed the same
  setup to assert facts already provable together: the two Claude-connector
  checks (tool set + instructions), and the two apps-mode absence checks
  (widget-workflow + data-vs-widget, same context, both empty together).

1584 tests (was 1590); type-check/lint/format/check:agents all green.
@MQ37
MQ37 force-pushed the feat/discovery-guidance-actor-runnable branch from 33bc97c to ba3722a Compare September 4, 2026 14:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants