diff --git a/src/components/FormTextArea.tsx b/src/components/FormTextArea.tsx
index 14a816081..a7d55df1b 100644
--- a/src/components/FormTextArea.tsx
+++ b/src/components/FormTextArea.tsx
@@ -54,31 +54,30 @@ export function FormTextArea({
return (
-
+
{name}
{helpText}
- {hidden > 0 && … (+{hidden} earlier lines)}
- {visible.length === 0 ? (
-
- {placeholder}
-
-
- ) : (
- visible.map((line, i) => (
-
- {line}
- {i === visible.length - 1 ? : null}
+
+ {hidden > 0 && … (+{hidden} earlier lines)}
+ {visible.length === 0 ? (
+
+ {placeholder}
+
- ))
- )}
+ ) : (
+ visible.map((line, i) => (
+
+ {line}
+ {i === visible.length - 1 ? : null}
+
+ ))
+ )}
+
);
}
diff --git a/src/components/HarnessWizard.tsx b/src/components/HarnessWizard.tsx
index df1cc549e..1026d8dca 100644
--- a/src/components/HarnessWizard.tsx
+++ b/src/components/HarnessWizard.tsx
@@ -867,8 +867,9 @@ function MemoryStep({
helpText="how should the harness remember conversations?"
options={MEMORY_OPTIONS}
focusedIndex={index}
+ selectedIndex={editing ? index : undefined}
/>
- {value.kind === "byo" && (
+ {editing && value.kind === "byo" && (
{value !== "" && (
-
- {`${value.length} chars · ctrl+d continues`}
-
+ {`${value.length} chars · ctrl+d continues`}
)}
);
diff --git a/src/handlers/harness/create/create.screen.test.tsx b/src/handlers/harness/create/create.screen.test.tsx
index 932a4a5d5..8d73c8ce8 100644
--- a/src/handlers/harness/create/create.screen.test.tsx
+++ b/src/handlers/harness/create/create.screen.test.tsx
@@ -75,6 +75,12 @@ describe("harness create wizard", () => {
// Step: prompt — type a prompt with a newline, then ctrl+d.
await waitForText(r.lastFrame, "type or paste the agent's instructions");
+ const promptFrame = r.lastFrame()!;
+ const promptHelpLine = promptFrame
+ .split("\n")
+ .findIndex((line) => line.includes("type or paste the agent's instructions"));
+ expect(promptFrame.split("\n")[promptHelpLine + 1]).toContain("╭");
+ expect(promptFrame).toContain("╰");
await r.write("You are helpful.");
await r.press("return"); // newline
await r.write("Be brief.");
@@ -323,7 +329,9 @@ describe("harness create wizard", () => {
await waitForText(r.lastFrame, "how should the harness remember conversations?");
await r.press("down"); // bring your own
await waitForText(r.lastFrame, "● bring your own");
+ expect(r.lastFrame()).not.toContain("Memory ARN");
await r.press("return"); // focus the memory arn field
+ await waitForText(r.lastFrame, "Memory ARN");
await r.press("return"); // empty → error
await waitForText(r.lastFrame, "enter the ARN of an existing AgentCore Memory");
await r.write("arn:aws:bedrock-agentcore:us-east-1:123:memory/m-1");
diff --git a/src/handlers/harness/update/update.screen.test.tsx b/src/handlers/harness/update/update.screen.test.tsx
index b8e793d51..40c851774 100644
--- a/src/handlers/harness/update/update.screen.test.tsx
+++ b/src/handlers/harness/update/update.screen.test.tsx
@@ -131,6 +131,36 @@ describe("harness update wizard", () => {
r.unmount();
});
+ test("reveals the current bring-your-own memory ARN only after enter", async () => {
+ const core = coreForUpdate();
+ const current = currentHarness();
+ const memoryArn = "arn:aws:bedrock-agentcore:us-east-1:123:memory/existing";
+ current.harness!.memory = {
+ agentCoreMemoryConfiguration: { arn: memoryArn },
+ };
+ core.harness.setGetResponse(current);
+ const r = renderScreen("/agentcore/harness/update/MyHarness-abc123", { core });
+
+ await waitForText(r.lastFrame, "● keep current");
+ await r.press("return");
+
+ await waitForText(r.lastFrame, "● bring your own");
+ expect(r.lastFrame()).not.toContain("Memory ARN");
+ expect(r.lastFrame()).not.toContain(memoryArn);
+
+ await r.press("return");
+ await waitForText(r.lastFrame, memoryArn);
+
+ await r.press("escape");
+ await waitFor(() => !(r.lastFrame() ?? "").includes(memoryArn));
+ expect(r.lastFrame()).not.toContain("Memory ARN");
+ expect(r.lastFrame()).toContain("● bring your own");
+
+ await r.press("return");
+ await waitForText(r.lastFrame, memoryArn);
+ r.unmount();
+ });
+
test("changing the model submits a request with just that field", async () => {
const core = coreForUpdate();
const r = renderScreen("/agentcore/harness/update/MyHarness-abc123", { core });
diff --git a/src/handlers/project/create/create.screen.test.tsx b/src/handlers/project/create/create.screen.test.tsx
index 01de58af9..4dbb19094 100644
--- a/src/handlers/project/create/create.screen.test.tsx
+++ b/src/handlers/project/create/create.screen.test.tsx
@@ -81,15 +81,16 @@ describe("project create wizard", () => {
await r.press("down"); // harness
await r.press("return");
- // Model step: providers and the selected provider's fields share one page.
+ // Model step: fields stay hidden until the provider is confirmed.
await waitForText(r.lastFrame, "choose a model");
expect(r.lastFrame()).toContain("● bedrock");
expect(r.lastFrame()).not.toContain("bedrock (recommended)");
expect(r.lastFrame()).toContain("○ openai");
expect(r.lastFrame()).toContain("○ gemini");
expect(r.lastFrame()).toContain("○ litellm");
- expect(r.lastFrame()).toContain(DEFAULT_MODEL_ID);
+ expect(r.lastFrame()).not.toContain(DEFAULT_MODEL_ID);
await r.press("return"); // focus model id
+ await waitForText(r.lastFrame, DEFAULT_MODEL_ID);
await r.press("return"); // accept model id
// Review: the summary names the project, type, model, and directory.
@@ -247,6 +248,32 @@ describe("project create wizard", () => {
r.unmount();
});
+ test("reveals model fields only after enter and hides them again on escape", async () => {
+ const r = renderScreen("/agentcore/project/create");
+
+ await waitForText(r.lastFrame, "name your project");
+ await r.write("ModelApp");
+ await r.press("return");
+ await r.press("down"); // harness
+ await r.press("return");
+ await waitForText(r.lastFrame, "choose a model");
+
+ await r.press("down"); // openai
+ await waitForText(r.lastFrame, "● openai");
+ expect(r.lastFrame()).not.toContain("model ID");
+ expect(r.lastFrame()).not.toContain("API key ARN");
+
+ await r.press("return");
+ await waitForText(r.lastFrame, "model ID");
+ expect(r.lastFrame()).toContain("API key ARN");
+
+ await r.press("escape");
+ await waitFor(() => !(r.lastFrame() ?? "").includes("model ID"));
+ expect(r.lastFrame()).not.toContain("API key ARN");
+ expect(r.lastFrame()).toContain("● openai");
+ r.unmount();
+ });
+
test("the model picker remains readable in an 80x24 terminal", async () => {
const r = renderScreen("/agentcore/project/create");
await r.resize(80, 24);
@@ -268,8 +295,8 @@ describe("project create wizard", () => {
expect(frame).toContain("○ openai");
expect(frame).toContain("○ gemini");
expect(frame).toContain("○ litellm");
- expect(frame).toContain("model ID");
- expect(frame).toContain(DEFAULT_MODEL_ID);
+ expect(frame).not.toContain("model ID");
+ expect(frame).not.toContain(DEFAULT_MODEL_ID);
expect(frame).toContain("[enter] continue");
expect(frame).toContain("[esc] back");
r.unmount();
diff --git a/src/handlers/project/create/screen.tsx b/src/handlers/project/create/screen.tsx
index 3fd5370f6..1ba2469e4 100644
--- a/src/handlers/project/create/screen.tsx
+++ b/src/handlers/project/create/screen.tsx
@@ -672,28 +672,30 @@ function ModelStep({
helpText="the provider and model that will power the harness"
options={options}
focusedIndex={providerIndex}
+ selectedIndex={focusedField !== null ? providerIndex : undefined}
/>
- {fields.map((field, fieldIndex) => (
- {
- onChange({
- ...value,
- configs: {
- ...value.configs,
- [value.provider]: { ...config, [field.key]: next },
- },
- });
- setError(null);
- }}
- focused={focusedField === fieldIndex}
- />
- ))}
+ {focusedField !== null &&
+ fields.map((field, fieldIndex) => (
+ {
+ onChange({
+ ...value,
+ configs: {
+ ...value.configs,
+ [value.provider]: { ...config, [field.key]: next },
+ },
+ });
+ setError(null);
+ }}
+ focused={focusedField === fieldIndex}
+ />
+ ))}
{error && (
{error}