PortRuntime is the top-level orchestrator for Claw Code. It tokenizes incoming prompts, scores them against the ported command and tool surfaces, assembles a RuntimeSession, and drives multi-turn execution loops — all without calling the upstream Claude Code binary.
route_prompt
prompt and scores every ported command and tool against those tokens. Returns up to limit matches sorted by relevance.
Selection logic
- Score all commands and all tools independently against the prompt tokens.
- Pick the single highest-scoring command (if any) and the single highest-scoring tool (if any) first.
- Fill remaining slots (up to
limit) from the leftover matches, ordered by score descending, then alphabetically.
The user-facing prompt to route. Punctuation (
/, -) is normalised to spaces before tokenising.Maximum number of
RoutedMatch entries to return.list[RoutedMatch]
Either
"command" or "tool".The ported module name (e.g.
"BashTool", "compact").Path or identifier in the upstream Claude Code source that this module mirrors.
Number of prompt tokens that matched this module’s name,
source_hint, or responsibility string.bootstrap_session
The prompt that drives routing and the initial engine turn.
Forwarded to
route_prompt.RuntimeSession
The original prompt string.
Workspace context snapshot (Python file count, archive availability, etc.).
Detected runtime environment (Python version, platform, test command, startup steps).
Full structured output from the workspace setup run.
The system-initialisation message passed to the engine (mirrors Claude Code’s
system_init).Structured log of every runtime phase (context, registry, routing, execution, turn, session store).
Matches returned by
route_prompt.Result of the single
QueryEnginePort.submit_message call.Output messages from each matched command’s
.execute(prompt) call.Output messages from each matched tool’s
.execute(prompt) call.All event dicts yielded by
stream_submit_message for this turn.Absolute path of the
.json file written by persist_session().RuntimeSession.as_markdown()
run_turn_loop
QueryEnginePort through up to max_turns consecutive turns. Each turn after the first appends [turn N] to the prompt string so the engine can distinguish continuation turns.
Early exit: if any TurnResult.stop_reason is not "completed", the loop stops immediately and returns the results collected so far.
The base prompt. Routing is performed once; the same matched commands and tools are reused across all turns.
Forwarded to
route_prompt.Upper bound on the number of engine turns to execute.
When
True, each turn’s output is serialised as JSON via QueryEngineConfig.structured_output.list[TurnResult]
Each entry is a TurnResult from a single engine turn. See QueryEnginePort.submit_message for the full field reference.
run_turn_loop creates a fresh QueryEnginePort from the current workspace for every call. It does not resume a previously persisted session. Use QueryEnginePort.from_saved_session directly if you need to continue an existing session.Permission inference
PortRuntime automatically gates bash-adjacent tools during bootstrap_session. Any matched tool whose name contains "bash" (case-insensitive) receives a PermissionDenial with the reason:
"destructive shell execution remains gated in the Python port"
These denials are passed to the engine as denied_tools and appear in TurnResult.permission_denials. To apply custom permission rules, construct a ToolPermissionContext and call QueryEnginePort.submit_message directly instead of using bootstrap_session. See ToolPermissionContext for details.