Repository navigation
fix(explorer): take entity ids as one percent-encoded path segment, so bundle ids resolve - #25
Merged
Merged
Conversation
earakely-scale
approved these changes
Sep 30, 2026
…o bundle ids resolve
Bundle ids are @<namespace>/… paths (@local/<package>/<name>, or a bundle
folder's whole path), and a bundle run's instance ids are too. The explorer's
id routes took a single-segment {entity_id} / {task_id}, and the ASGI server
decodes %2F before routing, so every bundle entity 404'd: the hello example
could be listed but not opened, run or versioned.
An id in an explorer URL is now one percent-encoded path segment, '/' sent as
%2F, which is also what the published OpenAPI path parameters require.
EncodedIdRouting routes the entity and instance paths on the raw path with
%2F and %25 kept escaped, and the EntityId parameter type decodes the matched
segment. Flat ids are unchanged. A path converter was not used: it misroutes
ids whose trailing segments look like a route (a folder named "runs" or
"instances"). The UI now encodes the id at the 7 call sites that did not.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
route_path keeps %25 escaped so an id is decoded exactly once; the HTTP cases only covered slashes and unicode. Bare ids weird%2Fbare and 100%25done join the get/versions matrix, and a task id holding both runs the run routes. Decoding %25 in the router fails all of them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
polakamtejas
force-pushed
the
tejas/explorer-encoded-entity-ids
branch
from
September 30, 2026 21:29
b11d910 to
2c9d29a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bundle entities can't be opened in the explorer. Bundle ids are
@<namespace>/…paths:@local/<package>/<name>for an installed bundle, or the bundle folder's whole path (@local/~/work/triage/tickets). A bundle run's instance ids look the same (@local/agentenv-framework/hello/hello-5xlcvxjq). The explorer's id routes took a single-segment{entity_id}/{task_id}, and uvicorn decodes%2Fbefore routing. So every bundle entity 404'd, raw or encoded: thehelloexample can be listed but opening it shows "Failed to fetch (404)", and it can't be run or versioned.The contract: an id in an explorer URL is one percent-encoded path segment, with
/sent as%2F(encodeURIComponent). The published OpenAPI already implies this: OpenAPI 3.1 path parameter values must not contain an unescaped/. The UI already builds its own page URLs this way (/tasks/%40local%2F…). GitLab (projects/group%2Fproject) and npm (@scope%2fname) address names like these the same way.Changes
explorer/entity_ids.py(new):route_pathdecodes a raw path but keeps%2Fand%25escaped.EncodedIdRoutingis ASGI middleware that routes onroute_path(raw_path), only under/api/v1/{artifacts,envs,tasks,agents,evals,task-instances}/. Plugin routes elsewhere see exactly what they see today.EntityIdis the path-parameter type that decodes the matched segment.EntityId:routers/common.py)task_idinrouters/runs.py(10 routes), plusinstance_idon the instance and progress routestask_instance_idon conversationsenv-detail-page,run-step-progress,task-detail-page,universe-detail-page)./, so a plugin route under the core prefixes receives the segment encoded and decodes it withurllib.parse.unquote.Flat ids are unchanged: with no
%2Fin the path, the middleware changes nothing. The OpenAPI paths are byte-identical; the id parameters gain a description of the encoding.Why not
{id:path}: ids are opaque and built from user folder names, so a:pathconverter misroutes real ids, and it does so silently with a 200:{id:path}@local/~/stuff/instances/foofooof task@local/~/stuff"runs@local/~/triage"( ) & + , @, accents, a literal%2For#Why not
?id=: also unambiguous, but it changes the public URL of every id route./objects/content?object_url=already works this way for object URLs.Notes
/tasks/@local/a/b) is a 404 by design: clients encode.%2Fthrough (ApacheAllowEncodedSlashes NoDecode; nginxproxy_passwithout a URI part). The local explorer has no proxy.EntityIdis not exported for plugins. Exporting it would add to the plugin surface, and no explorer plugin exists yet.check_plugin_api.py: no break.routers/common.pyandapp.py(route summaries). Whichever lands second needs a small rebase.^[a-zA-Z0-9_\-][a-zA-Z0-9_.:@\-]*$) rejects/today, and future shared-registry namespaces.Testing
tst/unit/explorer/entity_ids_test.py:route_pathround-trips 8 ids (flat, bundle, reserved-looking segments, spaces/&/+/,/@, unicode, a literal%2F,%25,#/?).explorer_app_test.py:%2For%25(weird%2Fbare,100%25done): get and versions return the id, and the unencoded form 404s.runs(and one holding a literal%2Fand%25) goes through every run route: start run, start runs, list runs, instances, instance, progress, run groups, run group, the stream reachingcomplete, and cancel.%25in the router, the double-decoding bug, fails all 11 literal-percent HTTP cases. The full unit tier passes except 2test_mcp_cli_buildertests that fail identically on main here: the generated CLI's interpreter has noclick, which is environmental.typecheck,test:smokeandbuild:staticpass.agent-env upwith the realhellobundle: opened@local/agentenv-framework/hello/hellofrom the Tasks Hub (404 before), clicked Start 1 Run, watched it complete 3/3 steps, and opened the instance. The CLI run's namespaced instance serves its detail, progress and conversations.🤖 Generated with Claude Code
The PR appears safe to merge; the prior missing HTTP coverage is now present.
What we checked:
Summary
The explorer now accepts bundle entity and instance IDs as one percent-encoded path segment, so those IDs can be looked up and used in run routes. The UI encodes IDs when it builds the affected API paths.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR A["UI encodes the ID"] --> B["One URL path segment"] B --> C["Middleware keeps encoded slashes together"] C --> D["Route decodes the ID"] D --> E["Store looks up the original ID"]Reviews (2) · Last reviewed commit: "test(explorer): cover ids with a literal..."