Skip to content

feat: add Runtime read-only TUI#1802

Merged
aidandaly24 merged 38 commits into
refactorfrom
feat/runtime-read-only-tui
Jul 23, 2026
Merged

feat: add Runtime read-only TUI#1802
aidandaly24 merged 38 commits into
refactorfrom
feat/runtime-read-only-tui

Conversation

@aidandaly24

@aidandaly24 aidandaly24 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Description

Adds an interactive, read-only TUI for the Runtime control-plane commands introduced in #1797.

The Runtime TUI provides route-based flows for:

  • Listing and inspecting Runtimes
  • Listing and inspecting immutable Runtime versions
  • Listing and inspecting Runtime endpoints

Runtime follows the established Harness interaction model. Bare Runtime groups and leaves open their corresponding TUI routes, operation flags keep commands headless, and --json suppresses interactive rendering. Nested endpoint and version flows preserve route history so Escape returns through the selectors and command menus in order.

This change also introduces a shared token-paged table picker used by Runtime and Harness. It centralizes loading, empty, error, retry, filtering, responsive columns, transition locking, pagination, resize resets, and Escape behavior while keeping each resource's API calls, columns, row mapping, sorting, labels, and destinations in its adapter.

Runtime detail and headless output omit SDK $metadata. The shared table filter accepts pasted text delivered as a single terminal input event.

Runtime invocation, mutations, deployment or project resolution, logs, traces, and other resource families remain outside this change.

Related Issue

N/A

Documentation PR

N/A. README command and interaction documentation is included in this PR.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Other (please describe):

Testing

Verified from current HEAD 023f2af:

  • bun test (319 passed, 0 failed)
  • bun run typecheck
  • bun run lint:check
  • bun run format:check
  • Manual terminal and TUI-harness verification of Runtime, version, and endpoint navigation

Checklist

  • I have read the CONTRIBUTING document
  • I have added necessary tests that prove the feature works
  • I have updated the documentation accordingly
  • I have added appropriate examples to the documentation
  • My changes generate no new warnings
  • No dependent changes are required

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@github-actions github-actions Bot added agentcore-harness-reviewing AgentCore Harness review in progress and removed agentcore-harness-reviewing AgentCore Harness review in progress labels Jul 22, 2026
@@ -0,0 +1,103 @@
import { useNavigate } from "react-router";

@aidandaly24 aidandaly24 Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I wouldn't be against sorting these into folders since I could picture this getting unwieldy as time goes on. The reason I held off was because I didn't know if maybe we just sort the primitive ones or if maybe we just want to sort into a folder called "pickers".

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.

I actually prefer a flat directory of components! I tend to think a flat directory is easier to maintain over time and that it makes it easier to find what you're looking for.

emptyPageMessage: string;
}

export function TokenPagedTablePicker<TItem, TRow extends Record<string, unknown>>({

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TokenPagedTablePicker centralizes the interaction state shared by Runtime and Harness resource pickers like terminal-derived page sizing, continuation-token history, loading/retry/empty states, page transitions, resize resets, filtering, selection, and keyboard navigation.

Resource-specific adapters still own their API calls, query keys, row mapping, columns, sorting, labels, selectors, and navigation destinations. This removes duplicated pagination state machines without forcing different resources into one data model, and gives the shared behavior one focused test suite.

@@ -0,0 +1,7 @@
export function withoutSdkMetadata(data: unknown): unknown {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This PR strips SDK $metadata at the Runtime handler boundary so both headless and TUI output remain resource-focused. I think we should decide whether transport metadata removal belongs in Core, JsonRenderer, or another shared normalization layer, then apply this contract consistently across resource families. What do you think, it could be done in this PR or a followup?

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.

I agree that we shouldn't show $metadata. Let's handle globally in a follow up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. I’ll remove the Runtime-specific handling from this PR and address $metadata globally in a follow-up. I'll add a backlog item for it.

@codecov-commenter

codecov-commenter commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.73154% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.84%. Comparing base (3bf082c) to head (4ffe40a).

Files with missing lines Patch % Lines
src/handlers/runtime/get/screen.tsx 98.41% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           refactor    #1802      +/-   ##
============================================
+ Coverage     94.08%   94.84%   +0.75%     
============================================
  Files           130      143      +13     
  Lines          6631     7120     +489     
============================================
+ Hits           6239     6753     +514     
+ Misses          392      367      -25     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@AlexanderRichey AlexanderRichey 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.

Looks good! Blocking things are:

  • Simplifying the column handling where TokenPagedTablePicker is used. We might want to do something similar to what you have here in a later PR, but for now let's try to keep things as simple as possible.
  • Cleaning up some of the tests, particularly the ones for the RouterScreen.

showFooter = true,
emptyMessage = "No data",
focus = true,
selectionResetKey,

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.

What's this for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This handles terminal-height resizes. When pageSize changes, pagination resets but DataTable would otherwise retain its selected row and internal page. This resets those states while preserving filter and sort state.

{searchMode ? (
<Text>
<Text color={theme.colors.primary}>/ Filter: </Text>
<Text color={theme.colors.primary}>/ {searchPlaceholder}: </Text>

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.

What makes changing this necessary?

Comment thread src/components/ui/key-hint/KeyHint.tsx Outdated
}

return keys.filter((_, index) => selected.has(index));
}

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.

These changes add a lot of complexity. I see the upshot, but I'm not sure that now is the time to add all this. Let me pull these changes down and test the behavior a bit.

@@ -0,0 +1,103 @@
import { useNavigate } from "react-router";

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.

I actually prefer a flat directory of components! I tend to think a flat directory is easier to maintain over time and that it makes it easier to find what you're looking for.

};
}}
toRow={toRow}
columns={(terminalColumns) => {

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.

We're adding a whole lot of complexity here. I tend to think we shouldn't worry about this problem now. When we do take on this problem, we should solve it in a general way and not in a way that will require every user of DataTable to solve the problem again, as I think the current approach would.

breadcrumb={breadcrumb}
keyHints={[
{ key: "↑↓/kj", label: "navigate" },
...(error && onRetry ? [{ key: "r", label: "retry" }] : []),

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.

Should this be "retry" or "reload"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I intentionally used "retry" because this action only appears after a failed query and invokes refetch to retry that request. I could switch it to "reload" if that makes more sense or you think would be more consistent for other places where we may want to use the "r" letter.

Comment thread src/components/HarnessVersionPicker.tsx Outdated
const showCreatedAt = terminalColumns >= 90;
const statusWidth = showStatus ? 20 : 0;
const createdAtWidth = showCreatedAt ? 30 : 0;
const versionWidth = Math.max(8, terminalColumns - 2 - statusWidth - createdAtWidth);

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.

Same here. I like the idea of a component that handles this stuff, but I think we need to simplify the approach here a bit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The version picker now passes static columns without terminal-specific width calculations.

Comment thread src/components/RouterScreen.test.tsx Outdated
agentRuntimeVersion: "7",
createdAt: new Date("2026-07-19T01:02:03.000Z"),
lastUpdatedAt: new Date("2026-07-20T12:34:56.000Z"),
roleArn: "arn:aws:iam::123456789012:role/runtime-role",

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.

All of this stuff seems out of scope for RouterScreen tests. We should be testing the behavior of the router screen and not all this other stuff here.

@@ -0,0 +1,7 @@
export function withoutSdkMetadata(data: unknown): unknown {

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.

I agree that we shouldn't show $metadata. Let's handle globally in a follow up.

@aidandaly24
aidandaly24 force-pushed the feat/runtime-read-only-tui branch from f7c3938 to 8a45204 Compare July 23, 2026 04:42

@AlexanderRichey AlexanderRichey 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.

This is looking good! Let's merge this one. We have one follow up to handle which is to get the column lengths right in the data tables. Let's discuss offline on a strategy for this.

Hweinstock
Hweinstock previously approved these changes Jul 23, 2026
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";

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.

unrelated to change here but noticed the path has data-table. Should we swap to dataTable to be consistent in another PR?

onEscape?: () => void;
}

// HarnessEndpointPicker fetches a harness's endpoints and renders them as a navigable

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.

Patrick had a nice comment a bit ago about standardizing on TSDoc format. Do we think its worth trying to follow that standard?

I would recommend using TSDoc format consistently so you can get auto generated docs. I've spotted some functions throughout that just use //.

AlexanderRichey/agentcore-cli#3 (comment)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated the exported Harness picker documentation to TSDoc for consistency. I left implementation-only comments as regular comments.

Comment thread src/components/HarnessPicker.tsx Outdated

return (
<Layout
<TokenPagedTablePicker

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.

I really like this abstraction btw. One nit is the name TokenPagedTablePicker feels kinda odd to me. What do we think of PaginatedTablePicker? (also open to original name if we think it makes more sense)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I agree the name is currently a mouthful. Updating to your suggestion it is better.

@aidandaly24
aidandaly24 dismissed stale reviews from Hweinstock and AlexanderRichey via 432a397 July 23, 2026 19:01
@aidandaly24
aidandaly24 force-pushed the feat/runtime-read-only-tui branch from 8394594 to 432a397 Compare July 23, 2026 19:01
@aidandaly24

Copy link
Copy Markdown
Contributor Author

We have one follow up to handle which is to get the column lengths right in the data tables. Let's discuss offline on a strategy for this.

Sounds good. For the follow-up, my initial direction is to make DataTable own terminal-aware column sizing rather than having each picker calculate widths. Column definitions could declare sizing intent such as minimum/maximum width and priority, while DataTable allocates the available width, truncates predictably, and hides optional columns only when necessary. We can align offline on the exact format and validate it.

@aidandaly24
aidandaly24 merged commit 272e50e into refactor Jul 23, 2026
8 checks passed
@aidandaly24
aidandaly24 deleted the feat/runtime-read-only-tui branch July 23, 2026 19:56
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.

4 participants