diff --git a/src/cli/tui/hooks/__tests__/useDevDeploy.test.tsx b/src/cli/tui/hooks/__tests__/useDevDeploy.test.tsx
index 690055c55..d4130ccd0 100644
--- a/src/cli/tui/hooks/__tests__/useDevDeploy.test.tsx
+++ b/src/cli/tui/hooks/__tests__/useDevDeploy.test.tsx
@@ -36,10 +36,11 @@ vi.mock('../../../operations/deploy/change-detection', () => ({
}));
function Harness({ skip }: { skip?: boolean }) {
- const { steps, isComplete, error } = useDevDeploy({ skip });
+ const { steps, isComplete, error, managedMemoryNotice } = useDevDeploy({ skip });
return (
- steps:{steps.length} isComplete:{String(isComplete)} error:{error ?? 'null'}
+ steps:{steps.length} isComplete:{String(isComplete)} error:{error ?? 'null'} notice:
+ {managedMemoryNotice ?? 'null'}
);
}
@@ -108,6 +109,31 @@ describe('useDevDeploy', () => {
});
});
+ it('surfaces the managed-memory heads-up from the onNotice callback', async () => {
+ mockHandleDeploy.mockImplementation((opts: { onNotice?: (message: string) => void }) => {
+ opts.onNotice?.('Managed memory: this harness automatically provisions a dedicated AgentCore Memory resource');
+ return Promise.resolve({ success: true });
+ });
+
+ const { lastFrame } = render();
+
+ await vi.waitFor(() => {
+ expect(lastFrame()).toContain('notice:Managed memory:');
+ expect(lastFrame()).toContain('isComplete:true');
+ });
+ });
+
+ it('leaves the managed-memory heads-up null when onNotice is not called', async () => {
+ mockHandleDeploy.mockResolvedValue({ success: true });
+
+ const { lastFrame } = render();
+
+ await vi.waitFor(() => {
+ expect(lastFrame()).toContain('isComplete:true');
+ });
+ expect(lastFrame()).toContain('notice:null');
+ });
+
it('populates steps from onProgress callback', async () => {
mockHandleDeploy.mockImplementation((opts: { onProgress?: (step: string, status: string) => void }) => {
opts.onProgress?.('Validate project', 'start');
diff --git a/src/cli/tui/hooks/useDevDeploy.ts b/src/cli/tui/hooks/useDevDeploy.ts
index a570676f4..d6615f8ee 100644
--- a/src/cli/tui/hooks/useDevDeploy.ts
+++ b/src/cli/tui/hooks/useDevDeploy.ts
@@ -18,6 +18,8 @@ export interface UseDevDeployResult {
isComplete: boolean;
error: string | undefined;
logPath: string | undefined;
+ /** Managed-memory heads-up surfaced by handleDeploy (null when not applicable) */
+ managedMemoryNotice: string | null;
}
export function useDevDeploy({ skip, ready = true }: UseDevDeployOptions = {}): UseDevDeployResult {
@@ -26,6 +28,7 @@ export function useDevDeploy({ skip, ready = true }: UseDevDeployOptions = {}):
const [deployDone, setDeployDone] = useState(false);
const [error, setError] = useState();
const [logPath, setLogPath] = useState();
+ const [managedMemoryNotice, setManagedMemoryNotice] = useState(null);
const hasStarted = useRef(false);
const onProgress = useCallback((stepName: string, status: 'start' | 'success' | 'error') => {
@@ -41,6 +44,10 @@ export function useDevDeploy({ skip, ready = true }: UseDevDeployOptions = {}):
setDeployMessages(prev => [...prev, msg]);
}, []);
+ const onNotice = useCallback((message: string) => {
+ setManagedMemoryNotice(message);
+ }, []);
+
useEffect(() => {
if (skip || !ready || hasStarted.current) return;
hasStarted.current = true;
@@ -78,6 +85,7 @@ export function useDevDeploy({ skip, ready = true }: UseDevDeployOptions = {}):
verbose: true,
onProgress,
onDeployMessage,
+ onNotice,
});
if (result.logPath) {
@@ -95,10 +103,10 @@ export function useDevDeploy({ skip, ready = true }: UseDevDeployOptions = {}):
};
void run();
- }, [skip, ready, onProgress, onDeployMessage]);
+ }, [skip, ready, onProgress, onDeployMessage, onNotice]);
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- skip is boolean, not nullable; || is the correct operator here
const isComplete = skip || deployDone;
- return { steps, deployMessages, isComplete, error, logPath };
+ return { steps, deployMessages, isComplete, error, logPath, managedMemoryNotice };
}
diff --git a/src/cli/tui/screens/dev/DevScreen.tsx b/src/cli/tui/screens/dev/DevScreen.tsx
index aa3abd89d..e52280b53 100644
--- a/src/cli/tui/screens/dev/DevScreen.tsx
+++ b/src/cli/tui/screens/dev/DevScreen.tsx
@@ -245,6 +245,7 @@ export function DevScreen(props: DevScreenProps) {
isComplete: deployComplete,
error: deployError,
logPath: deployLogPath,
+ managedMemoryNotice,
} = useDevDeploy({ skip: props.skipDeploy, ready: mode === 'deploying' });
const hasTransitionedFromDeployRef = useRef(false);
@@ -527,6 +528,11 @@ export function DevScreen(props: DevScreenProps) {
+ {managedMemoryNotice && !deployComplete && (
+
+ Note: {managedMemoryNotice}
+
+ )}
{hasStartedCfn && (
diff --git a/src/cli/tui/screens/harness/AddHarnessScreen.tsx b/src/cli/tui/screens/harness/AddHarnessScreen.tsx
index 3d94b85cb..60941f5f8 100644
--- a/src/cli/tui/screens/harness/AddHarnessScreen.tsx
+++ b/src/cli/tui/screens/harness/AddHarnessScreen.tsx
@@ -1423,7 +1423,10 @@ export function AddHarnessScreen({
initialValue="/mnt/data/"
onSubmit={wizard.setSessionStoragePath}
onCancel={() => wizard.goBack()}
- customValidation={value => (value.startsWith('/') ? true : 'Must be an absolute path')}
+ customValidation={value => {
+ const r = validateBYOMountPath(value);
+ return r === true ? true : r;
+ }}
/>
)}